Completed
Push — master ( c8eeda...8d0d2d )
by Nekrasov
04:10
created

BladeProvider::removeTemplateFolderFromViewPaths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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 = $_SERVER['DOCUMENT_ROOT'].'/'.$baseViewPath;
47
        static::$cachePath = $_SERVER['DOCUMENT_ROOT'].'/'.$cachePath;
48
49
        static::instantiateServiceContainer();
50
        static::instantiateViewFactory();
51
        static::registerBitrixDirectives();
52
53
        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...
54
        $arCustomTemplateEngines['blade'] = [
55
            'templateExt' => ['blade'],
56
            'function'    => 'renderBladeTemplate',
57
        ];
58
    }
59
60
    /**
61
     * Get view factory.
62
     *
63
     * @return Factory
64
     */
65
    public static function getViewFactory()
66
    {
67
        return static::$viewFactory;
68
    }
69
70
    /**
71
     * @return BladeCompiler
72
     */
73
    public function getCompiler()
74
    {
75
        return static::$container['blade.compiler'];
76
    }
77
78
    /**
79
     * Update paths where blade tries to find additional views.
80
     *
81
     * @param string $templateDir
82
     */
83
    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...
84
    {
85
        $finder = Container::getInstance()->make('view.finder');
86
87
        $currentPaths = $finder->getPaths();
88
        $newPaths = [$_SERVER['DOCUMENT_ROOT'].$templateDir];
89
90
        // Полностью перезаписывать пути нельзя, иначе вложенные компоненты + include перестанут работать.
91
        $newPaths = array_values(array_unique(array_merge($newPaths, $currentPaths)));
92
        if (!in_array(static::$baseViewPath, $newPaths)) {
93
            $newPaths[] = static::$baseViewPath;
94
        }
95
96
        // Необходимо очистить внутренний кэш ViewFinder-а
97
        // Потому что иначе если в родительском компоненте есть @include('foo'), то при вызове @include('foo') из дочернего,
98
        // он не будет искать foo в дочернем, а сразу подключит foo из родительского компонента
99
        $finder->flush();
100
101
        $finder->setPaths($newPaths);
102
    }
103
104
    /**
105
     * Undo addTemplateFolderToViewPaths
106
     *
107
     * @param string $templateDir
108
     */
109
    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...
110
    {
111
        $finder = Container::getInstance()->make('view.finder');
112
        $currentPaths = $finder->getPaths();
113
        $finder->setPaths(array_diff($currentPaths, [$_SERVER['DOCUMENT_ROOT'].$templateDir] ));
114
115
        // Необходимо очистить внутренний кэш ViewFinder-а
116
        // Потому что иначе если в дочернем компоненте есть @include('foo'), то при вызове @include('foo') в родительском
117
        // после подключения дочернего,
118
        // он не будет искать foo в родительском, а сразу подключит foo из дочернего компонента
119
        $finder->flush();
120
    }
121
122
    /**
123
     * Instantiate service container if it's not instantiated yet.
124
     */
125
    protected static function instantiateServiceContainer()
126
    {
127
        $container = Container::getInstance();
128
129
        if (!$container) {
130
            $container = new Container();
131
            Container::setInstance($container);
132
        }
133
134
        static::$container = $container;
135
    }
136
137
    /**
138
     * Instantiate view factory.
139
     */
140
    protected static function instantiateViewFactory()
141
    {
142
        static::createDirIfNotExist(static::$baseViewPath);
143
        static::createDirIfNotExist(static::$cachePath);
144
145
        $viewPaths = [
146
            static::$baseViewPath,
147
        ];
148
        $cache = static::$cachePath;
149
150
        $blade = new Blade($viewPaths, $cache, static::$container);
151
152
        static::$viewFactory = $blade->view();
153
        static::$viewFactory->addExtension('blade', 'blade');
154
    }
155
156
    /**
157
     * Create dir if it does not exist.
158
     *
159
     * @param string $path
160
     */
161
    protected static function createDirIfNotExist($path)
162
    {
163
        if (!file_exists($path)) {
164
            $mask = umask(0);
165
            mkdir($path, 0777, true);
166
            umask($mask);
167
        }
168
    }
169
170
    /**
171
     * Register bitrix directives.
172
     */
173
    protected static function registerBitrixDirectives()
174
    {
175
        $compiler = static::getCompiler();
176
177
        $endIf = function () {
178
            return '<?php endif; ?>';
179
        };
180
181
        $compiler->directive('component', function ($expression) {
182
            $expression = rtrim($expression, ')');
183
            $expression = ltrim($expression, '(');
184
185
            return '<?php $APPLICATION->IncludeComponent('.$expression.'); ?>';
186
        });
187
188
        $compiler->directive('block', function ($expression) {
189
            $expression = rtrim($expression, ')');
190
            $expression = ltrim($expression, '(');
191
192
            return '<?php ob_start(); $__bx_block = ' . $expression . '; ?>';
193
        });
194
195
        $compiler->directive('endblock', function () {
196
            return '<?php $APPLICATION->AddViewContent($__bx_block, ob_get_clean()); ?>';
197
        });
198
199
        $compiler->directive('lang', function ($expression) {
200
            return '<?= Bitrix\Main\Localization\Loc::getMessage('.$expression.') ?>';
201
        });
202
203
        $compiler->directive('auth', function () {
204
            return '<?php if($USER->IsAuthorized()): ?>';
205
        });
206
        $compiler->directive('guest', function () {
207
            return '<?php if(!$USER->IsAuthorized()): ?>';
208
        });
209
        $compiler->directive('admin', function () {
210
            return '<?php if($USER->IsAdmin()): ?>';
211
        });
212
213
        $compiler->directive('endauth', $endIf);
214
        $compiler->directive('endguest', $endIf);
215
        $compiler->directive('endadmin', $endIf);
216
217
        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...
218
    }
219
220
    /**
221
     * @param BladeCompiler $compiler
222
     */
223
    private static function registerHermitageDirectives($compiler)
224
    {
225
        $simpleDirectives = [
226
            'actionEditIBlockElement' => 'editIBlockElement',
227
            'actionDeleteIBlockElement' => 'deleteIBlockElement',
228
            'actionEditAndDeleteIBlockElement' => 'editAndDeleteIBlockElement',
229
230
            'actionEditIBlockSection' => 'editIBlockSection',
231
            'actionDeleteIBlockSection' => 'deleteIBlockSection',
232
            'actionEditAndDeleteIBlockSection' => 'editAndDeleteIBlockSection',
233
234
            'actionEditHLBlockElement' => 'editHLBlockElement',
235
            'actionDeleteHLBlockElement' => 'deleteHLBlockElement',
236
            'actionEditAndDeleteHLBlockElement' => 'editAndDeleteHLBlockElement',
237
238
            'actionAddForIBlock' => 'addForIBlock',
239
        ];
240 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...
241
            $compiler->directive($directive, function ($expression) use ($action) {
242
                $expression = rtrim($expression, ')');
243
                $expression = ltrim($expression, '(');
244
                return '<?php \Arrilot\BitrixHermitage\Action::' . $action . '($template, ' . $expression . '); ?>';
245
            });
246
        }
247
248
        $echoDirectives = [
249
            'actionAreaForIBlockElement' => 'areaForIBlockElement',
250
            'actionAreaForIBlockSection' => 'areaForIBlockSection',
251
            'actionAreaForHLBlockElement' => 'areaForHLBlockElement',
252
        ];
253 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...
254
            $compiler->directive($directive, function ($expression) use ($action) {
255
                $expression = rtrim($expression, ')');
256
                $expression = ltrim($expression, '(');
257
                return '<?= \Arrilot\BitrixHermitage\Action::' . $action . '($template, ' . $expression . '); ?>';
258
            });
259
        }
260
    }
261
}
262