CompilerEngine   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 100
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 10
lcom 2
cbo 6
dl 0
loc 100
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 8 1
A handleViewException() 0 36 5
A getBladeLineNumber() 0 12 2
A modifyViewsInTrace() 0 16 2
1
<?php
2
3
namespace Facade\Ignition\Views\Engines;
4
5
use Exception;
6
use Facade\Ignition\Exceptions\ViewException;
7
use Facade\Ignition\Exceptions\ViewExceptionWithSolution;
8
use Facade\Ignition\Views\Compilers\BladeSourceMapCompiler;
9
use Facade\Ignition\Views\Concerns\CollectsViewExceptions;
10
use Facade\IgnitionContracts\ProvidesSolution;
11
use Illuminate\Filesystem\Filesystem;
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Collection;
14
use ReflectionProperty;
15
use Throwable;
16
17
class CompilerEngine extends \Illuminate\View\Engines\CompilerEngine
18
{
19
    use CollectsViewExceptions;
20
21
    protected $currentPath = null;
22
23
    /**
24
     * Get the evaluated contents of the view.
25
     *
26
     * @param  string $path
27
     * @param  array $data
28
     *
29
     * @return string
30
     */
31
    public function get($path, array $data = [])
32
    {
33
        $this->currentPath = $path;
34
35
        $this->collectViewData($path, $data);
36
37
        return parent::get($path, $data);
38
    }
39
40
    /**
41
     * Handle a view exception.
42
     *
43
     * @param  \Exception $baseException
44
     * @param  int $obLevel
45
     *
46
     * @return void
47
     *
48
     * @throws \Exception
49
     */
50
    protected function handleViewException(Throwable $baseException, $obLevel)
51
    {
52
        while (ob_get_level() > $obLevel) {
53
            ob_end_clean();
54
        }
55
56
        if ($baseException instanceof ViewException) {
57
            throw $baseException;
58
        }
59
60
        $viewExceptionClass = ViewException::class;
61
62
        if (in_array(ProvidesSolution::class, class_implements($baseException))) {
63
            $viewExceptionClass = ViewExceptionWithSolution::class;
64
        }
65
66
        $exception = new $viewExceptionClass(
67
            $this->getMessage($baseException),
68
            0,
69
            1,
70
            $this->getCompiledViewName($baseException->getFile()),
71
            $this->getBladeLineNumber($baseException->getFile(), $baseException->getLine()),
72
            $baseException
73
        );
74
75
        if ($viewExceptionClass === ViewExceptionWithSolution::class) {
76
            $exception->setSolution($baseException->getSolution());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Throwable as the method getSolution() does only exist in the following implementations of said interface: Facade\Ignition\Exceptions\InvalidConfig.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
77
        }
78
79
        $this->modifyViewsInTrace($exception);
80
81
        $exception->setView($this->getCompiledViewName($baseException->getFile()));
82
        $exception->setViewData($this->getCompiledViewData($baseException->getFile()));
83
84
        throw $exception;
85
    }
86
87
    protected function getBladeLineNumber(string $compiledPath, int $exceptionLineNumber): int
88
    {
89
        $viewPath = $this->getCompiledViewName($compiledPath);
90
91
        if (! $viewPath) {
92
            return $exceptionLineNumber;
93
        }
94
95
        $sourceMapCompiler = new BladeSourceMapCompiler(app(Filesystem::class), 'not-needed');
96
97
        return $sourceMapCompiler->detectLineNumber($viewPath, $exceptionLineNumber);
98
    }
99
100
    protected function modifyViewsInTrace(ViewException $exception)
101
    {
102
        $trace = Collection::make($exception->getPrevious()->getTrace())
103
            ->map(function ($trace) {
104
                if ($compiledData = $this->findCompiledView(Arr::get($trace, 'file', ''))) {
105
                    $trace['file'] = $compiledData['path'];
106
                    $trace['line'] = $this->getBladeLineNumber($trace['file'], $trace['line']);
107
                }
108
109
                return $trace;
110
            })->toArray();
111
112
        $traceProperty = new ReflectionProperty('Exception', 'trace');
113
        $traceProperty->setAccessible(true);
114
        $traceProperty->setValue($exception, $trace);
115
    }
116
}
117