Completed
Push — master ( bdbaad...c631a8 )
by Nekrasov
09:44
created

BladeProvider::isAbsolutePath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 2
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Arrilot\BitrixBlade;
4
5
use Illuminate\Container\Container;
6
use Illuminate\Contracts\View\Factory;
7
8
class BladeProvider
9
{
10
    /**
11
     * Path to a folder view common view can be stored.
12
     *
13
     * @var string
14
     */
15
    protected static $baseViewPath;
16
17
    /**
18
     * Local path to blade cache storage.
19
     *
20
     * @var string
21
     */
22
    protected static $cachePath;
23
24
    /**
25
     * View factory.
26
     *
27
     * @var Factory
28
     */
29
    protected static $viewFactory;
30
31
    /**
32
     * Service container factory.
33
     *
34
     * @var Container
35
     */
36
    protected static $container;
37
38
    /**
39
     * Register blade engine in Bitrix.
40
     *
41
     * @param string $baseViewPath
42
     * @param string $cachePath
43
     */
44
    public static function register($baseViewPath = 'local/views', $cachePath = 'bitrix/cache/blade')
0 ignored issues
show
Coding Style introduced by
register uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
45
    {
46
        static::$baseViewPath = static::isAbsolutePath($baseViewPath) ? $baseViewPath : $_SERVER['DOCUMENT_ROOT'].'/'.$baseViewPath;
47
        static::$cachePath = static::isAbsolutePath($cachePath) ? $cachePath : $_SERVER['DOCUMENT_ROOT'].'/'.$cachePath;
48
        static::instantiateServiceContainer();
49
        static::instantiateViewFactory();
50
        static::registerBitrixDirectives();
51
52
        global $arCustomTemplateEngines;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
53
        $arCustomTemplateEngines['blade'] = [
54
            'templateExt' => ['blade'],
55
            'function'    => 'renderBladeTemplate',
56
        ];
57
    }
58
59
    protected static function isAbsolutePath($path)
60
    {
61
        return $path && ($path[0] === DIRECTORY_SEPARATOR || preg_match('~\A[A-Z]:(?![^/\\\\])~i', $path) > 0);
62
    }
63
64
    /**
65
     * Get view factory.
66
     *
67
     * @return Factory
68
     */
69
    public static function getViewFactory()
70
    {
71
        return static::$viewFactory;
72
    }
73
74
    /**
75
     * @return BladeCompiler
76
     */
77
    public function getCompiler()
78
    {
79
        return static::$container['blade.compiler'];
80
    }
81
82
    /**
83
     * Update paths where blade tries to find additional views.
84
     *
85
     * @param string $templateDir
86
     */
87
    public static function addTemplateFolderToViewPaths($templateDir)
0 ignored issues
show
Coding Style introduced by
addTemplateFolderToViewPaths uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
88
    {
89
        $finder = Container::getInstance()->make('view.finder');
90
91
        $currentPaths = $finder->getPaths();
92
        $newPaths = [$_SERVER['DOCUMENT_ROOT'].$templateDir];
93
94
        // Полностью перезаписывать пути нельзя, иначе вложенные компоненты + include перестанут работать.
95
        $newPaths = array_values(array_unique(array_merge($newPaths, $currentPaths)));
96
        if (!in_array(static::$baseViewPath, $newPaths)) {
97
            $newPaths[] = static::$baseViewPath;
98
        }
99
100
        // Необходимо очистить внутренний кэш ViewFinder-а
101
        // Потому что иначе если в родительском компоненте есть @include('foo'), то при вызове @include('foo') из дочернего,
102
        // он не будет искать foo в дочернем, а сразу подключит foo из родительского компонента
103
        $finder->flush();
104
105
        $finder->setPaths($newPaths);
106
    }
107
108
    /**
109
     * Undo addTemplateFolderToViewPaths
110
     *
111
     * @param string $templateDir
112
     */
113
    public static function removeTemplateFolderFromViewPaths($templateDir)
0 ignored issues
show
Coding Style introduced by
removeTemplateFolderFromViewPaths uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
114
    {
115
        $finder = Container::getInstance()->make('view.finder');
116
        $currentPaths = $finder->getPaths();
117
        $finder->setPaths(array_diff($currentPaths, [$_SERVER['DOCUMENT_ROOT'].$templateDir] ));
118
119
        // Необходимо очистить внутренний кэш ViewFinder-а
120
        // Потому что иначе если в дочернем компоненте есть @include('foo'), то при вызове @include('foo') в родительском
121
        // после подключения дочернего,
122
        // он не будет искать foo в родительском, а сразу подключит foo из дочернего компонента
123
        $finder->flush();
124
    }
125
126
    /**
127
     * Instantiate service container if it's not instantiated yet.
128
     */
129
    protected static function instantiateServiceContainer()
130
    {
131
        $container = Container::getInstance();
132
133
        if (!$container) {
134
            $container = new Container();
135
            Container::setInstance($container);
136
        }
137
138
        static::$container = $container;
139
    }
140
141
    /**
142
     * Instantiate view factory.
143
     */
144
    protected static function instantiateViewFactory()
145
    {
146
        static::createDirIfNotExist(static::$baseViewPath);
147
        static::createDirIfNotExist(static::$cachePath);
148
149
        $viewPaths = [
150
            static::$baseViewPath,
151
        ];
152
        $cache = static::$cachePath;
153
154
        $blade = new Blade($viewPaths, $cache, static::$container);
155
156
        static::$viewFactory = $blade->view();
157
        static::$viewFactory->addExtension('blade', 'blade');
158
    }
159
160
    /**
161
     * Create dir if it does not exist.
162
     *
163
     * @param string $path
164
     */
165
    protected static function createDirIfNotExist($path)
166
    {
167
        if (!file_exists($path)) {
168
            $mask = umask(0);
169
            mkdir($path, 0777, true);
170
            umask($mask);
171
        }
172
    }
173
174
    /**
175
     * Register bitrix directives.
176
     */
177
    protected static function registerBitrixDirectives()
178
    {
179
        $compiler = static::getCompiler();
180
181
        $endIf = function () {
182
            return '<?php endif; ?>';
183
        };
184
185
        $compiler->directive('component', function ($expression) {
186
            $expression = rtrim($expression, ')');
187
            $expression = ltrim($expression, '(');
188
189
            return '<?php $APPLICATION->IncludeComponent('.$expression.'); ?>';
190
        });
191
192
        $compiler->directive('bxComponent', function ($expression) {
193
            $expression = rtrim($expression, ')');
194
            $expression = ltrim($expression, '(');
195
196
            return '<?php $APPLICATION->IncludeComponent('.$expression.'); ?>';
197
        });
198
199
        $compiler->directive('block', function ($expression) {
200
            $expression = rtrim($expression, ')');
201
            $expression = ltrim($expression, '(');
202
203
            return '<?php ob_start(); $__bx_block = ' . $expression . '; ?>';
204
        });
205
206
        $compiler->directive('endblock', function () {
207
            return '<?php $APPLICATION->AddViewContent($__bx_block, ob_get_clean()); ?>';
208
        });
209
210
        $compiler->directive('lang', function ($expression) {
211
            return '<?= Bitrix\Main\Localization\Loc::getMessage('.$expression.') ?>';
212
        });
213
214
        $compiler->directive('auth', function () {
215
            return '<?php if($USER->IsAuthorized()): ?>';
216
        });
217
        $compiler->directive('guest', function () {
218
            return '<?php if(!$USER->IsAuthorized()): ?>';
219
        });
220
        $compiler->directive('admin', function () {
221
            return '<?php if($USER->IsAdmin()): ?>';
222
        });
223
224
        $compiler->directive('endauth', $endIf);
225
        $compiler->directive('endguest', $endIf);
226
        $compiler->directive('endadmin', $endIf);
227
228
        static::registerHermitageDirectives($compiler);
0 ignored issues
show
Bug introduced by
Since registerHermitageDirectives() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of registerHermitageDirectives() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
229
    }
230
231
    /**
232
     * @param BladeCompiler $compiler
233
     */
234
    private static function registerHermitageDirectives($compiler)
235
    {
236
        $simpleDirectives = [
237
            'actionAddForIBlock' => 'addForIBlock',
238
        ];
239 View Code Duplication
        foreach ($simpleDirectives as $directive => $action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240
            $compiler->directive($directive, function ($expression) use ($action) {
241
                $expression = rtrim($expression, ')');
242
                $expression = ltrim($expression, '(');
243
                return '<?php \Arrilot\BitrixHermitage\Action::' . $action . '($template, ' . $expression . '); ?>';
244
            });
245
        }
246
247
        $echoDirectives = [
248
            'actionEditIBlockElement' => 'editIBlockElement',
249
            'actionDeleteIBlockElement' => 'deleteIBlockElement',
250
            'actionEditAndDeleteIBlockElement' => 'editAndDeleteIBlockElement',
251
252
            'actionEditIBlockSection' => 'editIBlockSection',
253
            'actionDeleteIBlockSection' => 'deleteIBlockSection',
254
            'actionEditAndDeleteIBlockSection' => 'editAndDeleteIBlockSection',
255
256
            'actionEditHLBlockElement' => 'editHLBlockElement',
257
            'actionDeleteHLBlockElement' => 'deleteHLBlockElement',
258
            'actionEditAndDeleteHLBlockElement' => 'editAndDeleteHLBlockElement',
259
        ];
260 View Code Duplication
        foreach ($echoDirectives as $directive => $action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
            $compiler->directive($directive, function ($expression) use ($action) {
262
                $expression = rtrim($expression, ')');
263
                $expression = ltrim($expression, '(');
264
                return '<?= \Arrilot\BitrixHermitage\Action::' . $action . '($template, ' . $expression . '); ?>';
265
            });
266
        }
267
    }
268
}
269