Вход Регистрация
Файл: vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Renderer/Mappers/BladeMapper.php
Строк: 415
<?php

namespace IlluminateFoundationExceptionsRendererMappers;

use 
IlluminateContractsFoundationApplication;
use 
IlluminateContractsViewFactory;
use 
IlluminateSupportArr;
use 
IlluminateSupportCollection;
use 
IlluminateViewCompilersBladeCompiler;
use 
IlluminateViewViewException;
use 
ReflectionClass;
use 
ReflectionProperty;
use 
SymfonyComponentErrorHandlerExceptionFlattenException;
use 
Throwable;

/*
 * This file contains parts of https://github.com/spatie/laravel-ignition.
 *
 * (c) Spatie <info@spatie.be>
 *
 * For the full copyright and license information, please review its LICENSE:
 *
 * The MIT License (MIT)
 *
 * Copyright (c) Spatie <info@spatie.be>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

class BladeMapper
{
    
/**
     * The view factory instance.
     *
     * @var IlluminateContractsViewFactory
     */
    
protected $factory;

    
/**
     * The Blade compiler instance.
     *
     * @var IlluminateViewCompilersBladeCompiler
     */
    
protected $bladeCompiler;

    
/**
     * Create a new Blade mapper instance.
     *
     * @param  IlluminateContractsViewFactory  $factory
     * @param  IlluminateViewCompilersBladeCompiler  $bladeCompiler
     */
    
public function __construct(Factory $factoryBladeCompiler $bladeCompiler)
    {
        
$this->factory $factory;
        
$this->bladeCompiler $bladeCompiler;
    }

    
/**
     * Map cached view paths to their original paths.
     *
     * @param  SymfonyComponentErrorHandlerExceptionFlattenException  $exception
     * @return SymfonyComponentErrorHandlerExceptionFlattenException
     */
    
public function map(FlattenException $exception)
    {
        while (
$exception->getClass() === ViewException::class) {
            if ((
$previous $exception->getPrevious()) === null) {
                break;
            }

            
$exception $previous;
        }

        
$trace = (new Collection($exception->getTrace()))
            ->
map(function ($frame) {
                if (
$originalPath $this->findCompiledView((string) Arr::get($frame'file'''))) {
                    
$frame['file'] = $originalPath;
                    
$frame['line'] = $this->detectLineNumber($frame['file'], $frame['line']);
                }

                return 
$frame;
            })->
toArray();

        return 
tap($exceptionfn () => (fn () => $this->trace $trace)->call($exception));
    }

    
/**
     * Find the compiled view file for the given compiled path.
     *
     * @param  string  $compiledPath
     * @return string|null
     */
    
protected function findCompiledView(string $compiledPath)
    {
        return 
once(fn () => $this->getKnownPaths())[$compiledPath] ?? null;
    }

    
/**
     * Get the list of known paths from the compiler engine.
     *
     * @return array<string, string>
     */
    
protected function getKnownPaths()
    {
        
$compilerEngineReflection = new ReflectionClass(
            
$bladeCompilerEngine $this->factory->getEngineResolver()->resolve('blade'),
        );

        if (! 
$compilerEngineReflection->hasProperty('lastCompiled') && $compilerEngineReflection->hasProperty('engine')) {
            
$compilerEngine $compilerEngineReflection->getProperty('engine');
            
$compilerEngine $compilerEngine->getValue($bladeCompilerEngine);
            
$lastCompiled = new ReflectionProperty($compilerEngine'lastCompiled');
            
$lastCompiled $lastCompiled->getValue($compilerEngine);
        } else {
            
$lastCompiled $compilerEngineReflection->getProperty('lastCompiled');
            
$lastCompiled $lastCompiled->getValue($bladeCompilerEngine);
        }

        
$knownPaths = [];
        foreach (
$lastCompiled as $lastCompiledPath) {
            
$compiledPath $bladeCompilerEngine->getCompiler()->getCompiledPath($lastCompiledPath);

            
$knownPaths[realpath($compiledPath ?? $lastCompiledPath)] = realpath($lastCompiledPath);
        }

        return 
$knownPaths;
    }

    
/**
     * Filter out the view data that should not be shown in the exception report.
     *
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    
protected function filterViewData(array $data)
    {
        return 
array_filter($data, function ($value$key) {
            if (
$key === 'app') {
                return ! 
$value instanceof Application;
            }

            return 
$key !== '__env';
        }, 
ARRAY_FILTER_USE_BOTH);
    }

    
/**
     * Detect the line number in the original blade file.
     *
     * @param  string  $filename
     * @param  int  $compiledLineNumber
     * @return int
     */
    
protected function detectLineNumber(string $filenameint $compiledLineNumber)
    {
        
$map $this->compileSourcemap((string) file_get_contents($filename));

        return 
$this->findClosestLineNumberMapping($map$compiledLineNumber);
    }

    
/**
     * Compile the source map for the given blade file.
     *
     * @param  string  $value
     * @return string
     */
    
protected function compileSourcemap(string $value)
    {
        try {
            
$value $this->addEchoLineNumbers($value);
            
$value $this->addStatementLineNumbers($value);
            
$value $this->addBladeComponentLineNumbers($value);

            
$value $this->bladeCompiler->compileString($value);

            return 
$this->trimEmptyLines($value);
        } catch (
Throwable $e) {
            
report($e);

            return 
$value;
        }
    }

    
/**
     * Add line numbers to echo statements.
     *
     * @param  string  $value
     * @return string
     */
    
protected function addEchoLineNumbers(string $value)
    {
        
$echoPairs = [['{{''}}'], ['{{{''}}}'], ['{!!''!!}']];

        foreach (
$echoPairs as $pair) {
            
// Matches {{ $value }}, {!! $value !!} and  {{{ $value }}} depending on $pair
            
$pattern sprintf('/(@)?%ss*(.+?)s*%s(r?n)?/s'$pair[0], $pair[1]);

            if (
preg_match_all($pattern$value$matchesPREG_OFFSET_CAPTURE)) {
                foreach (
array_reverse($matches[0]) as $match) {
                    
$position mb_strlen(substr($value0$match[1]));

                    
$value $this->insertLineNumberAtPosition($position$value);
                }
            }
        }

        return 
$value;
    }

    
/**
     * Add line numbers to blade statements.
     *
     * @param  string  $value
     * @return string
     */
    
protected function addStatementLineNumbers(string $value)
    {
        
$shouldInsertLineNumbers preg_match_all(
            
'/B@(@?w+(?:::w+)?)([ t]*)(( ( (?>[^()]+) | (?3) )* ))?/x',
            
$value,
            
$matches,
            
PREG_OFFSET_CAPTURE
        
);

        if (
$shouldInsertLineNumbers) {
            foreach (
array_reverse($matches[0]) as $match) {
                
$position mb_strlen(substr($value0$match[1]));

                
$value $this->insertLineNumberAtPosition($position$value);
            }
        }

        return 
$value;
    }

    
/**
     * Add line numbers to blade components.
     *
     * @param  string  $value
     * @return string
     */
    
protected function addBladeComponentLineNumbers(string $value)
    {
        
$shouldInsertLineNumbers preg_match_all(
            
'/<s*x[-:]([w-:.]*)/mx',
            
$value,
            
$matches,
            
PREG_OFFSET_CAPTURE
        
);

        if (
$shouldInsertLineNumbers) {
            foreach (
array_reverse($matches[0]) as $match) {
                
$position mb_strlen(substr($value0$match[1]));

                
$value $this->insertLineNumberAtPosition($position$value);
            }
        }

        return 
$value;
    }

    
/**
     * Insert a line number at the given position.
     *
     * @param  int  $position
     * @param  string  $value
     * @return string
     */
    
protected function insertLineNumberAtPosition(int $positionstring $value)
    {
        
$before mb_substr($value0$position);

        
$lineNumber count(explode("n"$before));

        return 
mb_substr($value0$position)."|---LINE:{$lineNumber}---|".mb_substr($value$position);
    }

    
/**
     * Trim empty lines from the given value.
     *
     * @param  string  $value
     * @return string
     */
    
protected function trimEmptyLines(string $value)
    {
        
$value preg_replace('/^|---LINE:([0-9]+)---|$/m'''$value);

        return 
ltrim((string) $valuePHP_EOL);
    }

    
/**
     * Find the closest line number mapping in the given source map.
     *
     * @param  string  $map
     * @param  int  $compiledLineNumber
     * @return int
     */
    
protected function findClosestLineNumberMapping(string $mapint $compiledLineNumber)
    {
        
$map explode("n"$map);

        
$maxDistance 20;

        
$pattern '/|---LINE:(?P<line>[0-9]+)---|/m';

        
$lineNumberToCheck $compiledLineNumber 1;

        while (
true) {
            if (
$lineNumberToCheck $compiledLineNumber $maxDistance) {
                return 
min($compiledLineNumbercount($map));
            }

            if (
preg_match($pattern$map[$lineNumberToCheck] ?? ''$matches)) {
                return (int) 
$matches['line'];
            }

            
$lineNumberToCheck--;
        }
    }
}
Онлайн: 1
Реклама