Completed
Push — master ( 9b2c49...18696b )
by Igor
23:58
created

Native::compile()   F

Complexity

Conditions 20
Paths 168

Size

Total Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 71
rs 3.6
c 0
b 0
f 0
cc 20
nc 168
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @license MIT
4
 */
5
namespace Pivasic\Bundle\Template;
6
7
use Pivasic\Bundle\Template\Exception\FileNotFoundException;
8
9
/**
10
 * Class Native
11
 * @package Pivasic\Bundle\Template
12
 */
13
class Native
14
{
15
    /**
16
     * @param string $packageRoot
17
     * @param string $language
18
     * @param bool $cache
19
     */
20
    public function __construct(string $packageRoot, string $language = '', bool $cache = true)
21
    {
22
        $this->packageRoot = rtrim($packageRoot, '/');
23
        $this->language = $language;
24
        $this->cache = $cache;
25
        $this->isRouteView = false;
26
    }
27
28
    /**
29
     * Create content from template and data.
30
     *
31
     * @param string $name
32
     * @param array $data
33
     * @return string
34
     * @throws FileNotFoundException
35
     * @throws \RuntimeException
36
     */
37
    public function getContent(string $name, array $data = []): string
38
    {
39
        $cacheName = $name;
40
        if ('' == $name) {
41
            $this->isRouteView = true;
42
43
            $stack = debug_backtrace();
44
            foreach ($stack as $item) {
45
                if (false !== stripos($item['file'], DIRECTORY_SEPARATOR . 'Route' . DIRECTORY_SEPARATOR)) {
46
                    $cacheName = pathinfo($item['file'], PATHINFO_DIRNAME) . '/' . $name;
47
                    $cacheName = explode('Route' . DIRECTORY_SEPARATOR, $cacheName)[1];
48
                    $cacheName = 'route_' . str_replace(['/', '\\'], '_', $cacheName);
49
                    break;
50
                }
51
            }
52
        }
53
        $cacheName .= '_' . $this->language . '.html.php';
54
        $path = $this->packageRoot . '/view/_cache/' . str_replace('/', '_', $cacheName);
55
56
        if (!$this->cache || !file_exists($path)) {
57
            $code = $this->compile($name . '/view.html.php', true, true, true);
58
59
            $fh = fopen($path, 'wb');
60
            if (flock($fh, LOCK_EX)) {
61
                fwrite($fh, $code);
62
                flock($fh, LOCK_UN);
63
            }
64
            fclose($fh);
65
        }
66
67
        $fh = fopen($path, 'rb');
68
        if (flock($fh, LOCK_SH)) {
69
            $html = self::renderTemplate($path, $data);
70
71
            flock($fh, LOCK_UN);
72
            fclose($fh);
73
74
            return $html;
75
        }
76
77
        throw new \RuntimeException('Can\'t render template');
78
    }
79
80
    /**
81
     * Create solid template.
82
     *
83
     * @param string $name
84
     * @param bool $processLang
85
     * @param bool $processInclude
86
     * @param bool $processExtends
87
     * @return string
88
     * @throws FileNotFoundException
89
     */
90
    private function compile(string $name, bool $processLang, bool $processInclude, bool $processExtends): string
91
    {
92
        if ($this->isRouteView) {
93
            $this->isRouteView = false;
94
            $path = '';
95
            $stack = debug_backtrace();
96
            foreach ($stack as $item) {
97
                if (false !== stripos($item['file'], DIRECTORY_SEPARATOR . 'Route' . DIRECTORY_SEPARATOR)) {
98
                    $path = pathinfo($item['file'], PATHINFO_DIRNAME) . '/view.html.php';
99
                    if ($processLang) {
100
                        $storagePath = str_replace('view.html.php', '_lang/' . $this->language . '/data.php', $path);
101
                    }
102
                    break;
103
                }
104
            }
105
        } else {
106
            $path = $this->packageRoot . '/view/' . $name;
107
            if ($processLang) {
108
                $storagePath = str_replace('view.html.php', '', $path) . '_lang/' . $this->language . '/data.php';
109
            }
110
        }
111
112
        if (file_exists($path)) {
113
            ob_start();
114
            readfile($path);
115
            $code = ob_get_clean();
116
        } else {
117
            throw new FileNotFoundException($path);
118
        }
119
120
        if ($processLang && file_exists($storagePath)) {
121
            $storage = include $storagePath;
0 ignored issues
show
Bug introduced by
The variable $storagePath does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
122
            preg_match_all('/<!-- lang (.*) -->/', $code, $matchList);
123
            if (isset($matchList[1])) {
124
                foreach ($matchList[1] as $key => $index) {
125
                    $name = explode('>', $index);
126
                    $default = trim($name[1] ?? '');
127
                    $name = trim($name[0]);
128
                    if (!empty($matchList[0][$key]) && false !== strpos($code, $matchList[0][$key])) {
129
                        $code = str_replace($matchList[0][$key], $storage[$name] ?? $default, $code);
130
                    }
131
                }
132
            }
133
        }
134
135
        if ($processInclude) {
136
            preg_match_all('/<!-- include (.*) -->/', $code, $matchList);
137
            if (isset($matchList[1])) {
138
                foreach ($matchList[1] as $key => $template) {
139
                    if (!empty($matchList[0][$key]) && false !== strpos($code, $matchList[0][$key])) {
140
                        $template = trim($template);
141
                        $code = str_replace($matchList[0][$key], $this->compile($template . '/view.html.php', true, true, false), $code);
142
                    }
143
                }
144
            }
145
        }
146
147
        if ($processExtends) {
148
            preg_match_all('/<!-- extends (.*) -->/', $code, $matchList);
149
            if (isset($matchList[1][0])) {
150
                $template = trim($matchList[1][0]);
151
                $parentHtml = $this->compile($template . '/view.html.php', true, true, false);
152
153
                $code = str_replace($matchList[0][0], '', $code);
154
                $parentHtml = str_replace('<!-- section -->', $code, $parentHtml);
155
                $code = $parentHtml;
156
            }
157
        }
158
159
        return $code;
160
    }
161
162
    /**
163
     * Safe include. Used for scope isolation.
164
     *
165
     * @param string $__file__  File to include
166
     * @param array  $data      Data passed to template
167
     * @return string
168
     */
169
    private static function renderTemplate(string $__file__, array $data): string
170
    {
171
        ob_start();
172
        extract($data);
173
        include $__file__;
174
        return ob_get_clean();
175
    }
176
177
    private $packageRoot;
178
    private $language;
179
    private $cache;
180
    private $isRouteView;
181
}
182
183