├── .gitignore
├── .scrutinizer.yml
├── README.md
├── composer.json
├── docs
└── hermitage.md
└── src
├── Blade.php
├── BladeCompiler.php
├── BladeProvider.php
├── ViewFinder.php
└── functions.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 | /.idea
6 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | paths:
3 | - 'src/*'
4 | excluded_paths:
5 | - 'vendor/*'
6 | - 'tests/*'
7 | tools:
8 | php_cs_fixer:
9 | config: { level: psr2 }
10 | checks:
11 | php:
12 | code_rating: true
13 | duplication: true
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://packagist.org/packages/arrilot/bitrix-blade/)
2 | [](https://packagist.org/packages/Arrilot/bitrix-blade)
3 | [](https://scrutinizer-ci.com/g/arrilot/bitrix-blade/)
4 |
5 | # Данный пакет больше активно не поддерживается
6 |
7 | Причина - мы больше не используем Битрикс в своих проектах.
8 | Если вам интересен этот проект и вы хотите заняться его поддержкой - форкните его и создайте Issue в данном репозитории чтобы мы поместили здесь ссылку на форк.
9 |
10 | Форки:
11 | - https://github.com/Ge1i0N/bitrix-blade
12 |
13 | # Bitrix Blade - интеграция шаблонизатора Blade в Битрикс
14 |
15 | ## Установка
16 |
17 | 1)```composer require arrilot/bitrix-blade```
18 |
19 | 2) добавляем в init.php
20 |
21 | ```php
22 |
23 | use Arrilot\BitrixBlade\BladeProvider;
24 |
25 | require $_SERVER['DOCUMENT_ROOT']."/vendor/autoload.php";
26 |
27 | BladeProvider::register();
28 | ```
29 |
30 | ## Использование
31 |
32 | Заменяем шаблон компонента с `template.php` на `template.blade` и можно писать на `Blade`
33 |
34 | Директива `@include('path.to.view')` модифицирована следующим образом:
35 |
36 | 1. Сначала view ищется относительно директории текущего шаблона компонента (там где лежит template.blade)
37 | 2. Если не view там не нашёлся, то он ищется относительно базовой директории (по умолчанию `local/views`, но может быть указана другая при вызове `BladeProvider::register()`)
38 |
39 | ## Пользовательские директивы (custom directives)
40 |
41 | Для того чтобы добавить свою директиву, необходимо зарегистрировать её в компиляторе:
42 |
43 | ```
44 | $compiler = BladeProvider::getCompiler();
45 | $compiler->directive('directiveName', function ($expression) {
46 | return '...';
47 | });
48 | ```
49 | При установке пакета `BladeProvider::register()` за вас уже автоматически зарегистрировано некоторое количество полезных директив:
50 |
51 | 1. ```@bxComponent``` - аналог ```$APPLICATION->IncludeComponent()```
52 | 2. ```@block('key')``` и ```@endblock``` - всё что заключено между ними будет выведено в месте, где вызван метод ```$APPLICATION->ShowViewContent('key')```
53 | 3. ```@lang('key')``` - равносильно ```{!! Bitrix\Main\Localization\Loc::getMessage('key') !!} ```
54 | 4. ```@auth``` и ```@endauth``` - сокращенная запись `IsAuthorized()) ?> ... `
55 | 5. ```@guest``` и ```@endguest``` - аналогично, но проверка на неавторизованного юзера.
56 | 6. ```@admin``` и ```@endadmin``` - аналогично, но `$USER->IsAdmin()`
57 | 7. ```@csrf``` - сокращенная форма для ``````
58 | 8. [Директивы по работе с эрмитажем](docs/hermitage.md)
59 |
60 | ## Конфигурация
61 |
62 | При необходимости пути можно поменять в конфигурации.
63 | .settings_extra.php
64 | ```php
65 | 'bitrix-blade' => [
66 | 'value' => [
67 | 'baseViewPath' => '/absolute/path/or/path/from/document/root', // по умолчанию 'local/views'
68 | 'cachePath' => '/absolute/path/or/path/from/document/root', // по умолчанию 'local/cache/blade'
69 | ],
70 | 'readonly' => false,
71 | ],
72 | ```
73 |
74 | ## Очистка кэша
75 |
76 | Для обеспечения высокой скорости работы Blade кэширует скомпилированные шаблоны в php файлы.
77 | В большинстве случаев чистить этот кэш самостоятельно потребности нет, потому что блейд сверяет время модификации файлов шаблонов и кэша и самостоятеьно инвалидирует этот кэш.
78 | Однако в некоторых случаях (например при добавлении новой пользовательской директивы), этот кэш всё-же надо сбросить.
79 | Делается это методом ```BladeProvider::clearCache()```
80 |
81 |
82 | ## Некоторые моменты
83 |
84 | 1. Битрикс позволяет использовать сторонние шаблонизаторы только в шаблонах компонентов. Шаблоны сайтов только на php.
85 | 2. По понятным причинам наследованием шаблонов в полную силу воспользоваться не получится.
86 | 3. Традиционное расширение `.blade.php` использовать нельзя. Битрикс видя `.php` включает php движок.
87 | 4. Вместо `$this` в шаблоне следует использовать `$template` - например `$template->setFrameMode(true);`
88 | 5. Проверку `` прописывать в blade-шаблоне не нужно, она добавляется в скомпилированные view автоматически. Также вместе с этим выполняется и ```extract($arResult, EXTR_SKIP);```
89 | 6. Чтобы языковой файл из шаблона подключился, его (этот языковой файл) надо назвать как обычно - `template.php`
90 |
91 | ## Дополнительно
92 |
93 | PhpStorm
94 |
95 | 1. Чтобы включить подсветку синтаксиса в PhpStorm для .blade файлов нужно добавить это расширение в
96 | `Settings->Editor->File Types->Blade`
97 | 2. Чтобы PhpStorm понимал и подсвечивалл должным образом пользовательские директивы из этого пакета их можно добавить в него. Делается это в `Settings->Language & Frameworks->PHP->Blade->Directives`
98 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arrilot/bitrix-blade",
3 | "license": "MIT",
4 | "keywords": [
5 | "bitrix",
6 | "blade"
7 | ],
8 | "authors": [
9 | {
10 | "name": "Nekrasov Ilya",
11 | "email": "nekrasov.ilya90@gmail.com"
12 | }
13 | ],
14 | "homepage": "https://github.com/Arrilot/bitrix-blade",
15 | "require": {
16 | "php": ">=5.4.0",
17 | "illuminate/container": "5.*",
18 | "illuminate/view": "5.*",
19 | "illuminate/events": "5.*",
20 | "arrilot/bitrix-hermitage": "^1"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Arrilot\\BitrixBlade\\": "src/"
25 | },
26 | "files": [
27 | "src/functions.php"
28 | ]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/docs/hermitage.md:
--------------------------------------------------------------------------------
1 | #Директивы по работе с эрмитажем
2 |
3 | Используется вспомогательный пакет [https://github.com/arrilot/bitrix-hermitage](https://github.com/arrilot/bitrix-hermitage)
4 |
5 | Таблица соответвтия директив и методов пакета
6 |
7 | ```php
8 | @actionEditAndDeleteIBlockElement($element) => Action::editAndDeleteIBlockElement($template, $element),
9 | @actionEditIBlockElement($element) => Action::editIBlockElement($template, $element),
10 | @actionDeleteIBlockElement($element, $confirm = '...') => Action::deleteIBlockElement($template, $element, $confirm = '...'),
11 |
12 | @actionEditAndDeleteIBlockSection($section) => Action::editAndDeleteIBlockSection($template, $section),
13 | @actionEditIBlockSection($section) => Action::editIBlockSection($template, $section),
14 | @actionDeleteIBlockSection($section, $confirm = '...') => Action::deleteIBlockSection($template, $section, $confirm = '...'),
15 |
16 | @actionEditAndDeleteHLBlockElement($element) => Action::editAndDeleteHLBlockElement($template, $element) ,
17 | @actionEditHLBlockElement($element) => Action::editHLBlockElement($template, $element),
18 | @actionDeleteHLBlockElement($element, $confirm = '...') => Action::deleteHLBlockElement($template, $element, $confirm = '...'),
19 |
20 | @actionAddForIBlock($iblockId, [...]) => Action::addForIBlock($templateOrComponent, $iblockId, [...]),
21 | ```
22 |
--------------------------------------------------------------------------------
/src/Blade.php:
--------------------------------------------------------------------------------
1 | viewPaths = $viewPaths;
53 | $this->cachePath = $cachePath;
54 | $this->container = $container;
55 |
56 | $this->registerFilesystem();
57 | $this->registerEvents();
58 | $this->registerEngineResolver();
59 | $this->registerViewFinder();
60 | $this->registerFactory();
61 | }
62 |
63 | /**
64 | * Getter for view factory.
65 | *
66 | * @return Factory
67 | */
68 | public function view()
69 | {
70 | return $this->viewFactory;
71 | }
72 |
73 | /**
74 | * Register filesystem in container.
75 | *
76 | * @return void
77 | */
78 | public function registerFilesystem()
79 | {
80 | $this->container->singleton('files', function () {
81 | return new Filesystem();
82 | });
83 | }
84 |
85 | /**
86 | * Register events in container.
87 | *
88 | * @return void
89 | */
90 | public function registerEvents()
91 | {
92 | $this->container->singleton('events', function () {
93 | return new Dispatcher();
94 | });
95 | }
96 |
97 | /**
98 | * Register the engine resolver instance.
99 | *
100 | * @return void
101 | */
102 | public function registerEngineResolver()
103 | {
104 | $me = $this;
105 |
106 | $this->container->singleton('view.engine.resolver', function () use ($me) {
107 | $resolver = new EngineResolver();
108 |
109 | $me->registerPhpEngine($resolver);
110 | $me->registerBladeEngine($resolver);
111 |
112 | return $resolver;
113 | });
114 | }
115 |
116 | /**
117 | * Register the PHP engine implementation.
118 | *
119 | * @param EngineResolver $resolver
120 | *
121 | * @return void
122 | */
123 | public function registerPhpEngine($resolver)
124 | {
125 | $resolver->register('php', function () {
126 | return new PhpEngine();
127 | });
128 | }
129 |
130 | /**
131 | * Register the Blade engine implementation.
132 | *
133 | * @param EngineResolver $resolver
134 | *
135 | * @return void
136 | */
137 | public function registerBladeEngine($resolver)
138 | {
139 | $me = $this;
140 | $app = $this->container;
141 |
142 | $this->container->singleton('blade.compiler', function ($app) use ($me) {
143 | $cache = $me->cachePath;
144 |
145 | return new BladeCompiler($app['files'], $cache);
146 | });
147 |
148 | $resolver->register('blade', function () use ($app) {
149 | return new CompilerEngine($app['blade.compiler'], $app['files']);
150 | });
151 | }
152 |
153 | /**
154 | * Register the view factory.
155 | */
156 | public function registerFactory()
157 | {
158 | $resolver = $this->container['view.engine.resolver'];
159 |
160 | $finder = $this->container['view.finder'];
161 |
162 | $factory = new Factory($resolver, $finder, $this->container['events']);
163 | $factory->setContainer($this->container);
164 |
165 | //$factory->share('app', $this->container);
166 | $this->viewFactory = $factory;
167 | }
168 |
169 | /**
170 | * Register the view finder implementation.
171 | *
172 | * @return void
173 | */
174 | public function registerViewFinder()
175 | {
176 | $me = $this;
177 | $this->container->singleton('view.finder', function ($app) use ($me) {
178 | $paths = $me->viewPaths;
179 |
180 | return new ViewFinder($app['files'], $paths);
181 | });
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/BladeCompiler.php:
--------------------------------------------------------------------------------
1 | ';
19 | $result .= '';
20 |
21 | return $result . parent::compileString($value);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/BladeProvider.php:
--------------------------------------------------------------------------------
1 | ['blade'],
59 | 'function' => 'renderBladeTemplate',
60 | ];
61 | }
62 |
63 | protected static function isAbsolutePath($path)
64 | {
65 | return $path && ($path[0] === DIRECTORY_SEPARATOR || preg_match('~\A[A-Z]:(?![^/\\\\])~i', $path) > 0);
66 | }
67 |
68 | /**
69 | * Get view factory.
70 | *
71 | * @return Factory
72 | */
73 | public static function getViewFactory()
74 | {
75 | return static::$viewFactory;
76 | }
77 |
78 | /**
79 | * @return BladeCompiler
80 | */
81 | public static function getCompiler()
82 | {
83 | return static::$container['blade.compiler'];
84 | }
85 |
86 | /**
87 | * Clear all compiled view files.
88 | */
89 | public static function clearCache()
90 | {
91 | $path = static::$cachePath;
92 |
93 | if (!$path) {
94 | throw new RuntimeException('Cache path is empty');
95 | }
96 |
97 | $success = true;
98 | foreach (glob("{$path}/*") as $view) {
99 | try {
100 | if (!@unlink($view)) {
101 | $success = false;
102 | }
103 | } catch (ErrorException $e) {
104 | $success = false;
105 | }
106 | }
107 |
108 | return $success;
109 | }
110 |
111 | /**
112 | * Update paths where blade tries to find additional views.
113 | *
114 | * @param string $templateDir
115 | */
116 | public static function addTemplateFolderToViewPaths($templateDir)
117 | {
118 | $finder = Container::getInstance()->make('view.finder');
119 |
120 | $currentPaths = $finder->getPaths();
121 | $newPaths = [$_SERVER['DOCUMENT_ROOT'].$templateDir];
122 |
123 | // Полностью перезаписывать пути нельзя, иначе вложенные компоненты + include перестанут работать.
124 | $newPaths = array_values(array_unique(array_merge($newPaths, $currentPaths)));
125 | if (!in_array(static::$baseViewPath, $newPaths)) {
126 | $newPaths[] = static::$baseViewPath;
127 | }
128 |
129 | // Необходимо очистить внутренний кэш ViewFinder-а
130 | // Потому что иначе если в родительском компоненте есть @include('foo'), то при вызове @include('foo') из дочернего,
131 | // он не будет искать foo в дочернем, а сразу подключит foo из родительского компонента
132 | $finder->flush();
133 |
134 | $finder->setPaths($newPaths);
135 | }
136 |
137 | /**
138 | * Undo addTemplateFolderToViewPaths
139 | *
140 | * @param string $templateDir
141 | */
142 | public static function removeTemplateFolderFromViewPaths($templateDir)
143 | {
144 | $finder = Container::getInstance()->make('view.finder');
145 | $currentPaths = $finder->getPaths();
146 | $finder->setPaths(array_diff($currentPaths, [$_SERVER['DOCUMENT_ROOT'].$templateDir] ));
147 |
148 | // Необходимо очистить внутренний кэш ViewFinder-а
149 | // Потому что иначе если в дочернем компоненте есть @include('foo'), то при вызове @include('foo') в родительском
150 | // после подключения дочернего,
151 | // он не будет искать foo в родительском, а сразу подключит foo из дочернего компонента
152 | $finder->flush();
153 | }
154 |
155 | /**
156 | * Instantiate service container if it's not instantiated yet.
157 | */
158 | protected static function instantiateServiceContainer()
159 | {
160 | $container = Container::getInstance();
161 |
162 | if (!$container) {
163 | $container = new Container();
164 | Container::setInstance($container);
165 | }
166 |
167 | static::$container = $container;
168 | }
169 |
170 | /**
171 | * Instantiate view factory.
172 | */
173 | protected static function instantiateViewFactory()
174 | {
175 | static::createDirIfNotExist(static::$baseViewPath);
176 | static::createDirIfNotExist(static::$cachePath);
177 |
178 | $viewPaths = [
179 | static::$baseViewPath,
180 | ];
181 | $cache = static::$cachePath;
182 |
183 | $blade = new Blade($viewPaths, $cache, static::$container);
184 |
185 | static::$viewFactory = $blade->view();
186 | static::$viewFactory->addExtension('blade', 'blade');
187 | }
188 |
189 | /**
190 | * Create dir if it does not exist.
191 | *
192 | * @param string $path
193 | */
194 | protected static function createDirIfNotExist($path)
195 | {
196 | if (!file_exists($path)) {
197 | $mask = umask(0);
198 | mkdir($path, 0777, true);
199 | umask($mask);
200 | }
201 | }
202 |
203 | /**
204 | * Register bitrix directives.
205 | */
206 | protected static function registerBitrixDirectives()
207 | {
208 | $compiler = static::getCompiler();
209 |
210 | $endIf = function () {
211 | return '';
212 | };
213 |
214 | $compiler->directive('bxComponent', function ($expression) {
215 | $expression = rtrim($expression, ')');
216 | $expression = ltrim($expression, '(');
217 |
218 | return 'IncludeComponent('.$expression.'); ?>';
219 | });
220 |
221 | $compiler->directive('block', function ($expression) {
222 | $expression = rtrim($expression, ')');
223 | $expression = ltrim($expression, '(');
224 |
225 | return '';
226 | });
227 |
228 | $compiler->directive('endblock', function () {
229 | return 'AddViewContent($__bx_block, ob_get_clean()); ?>';
230 | });
231 |
232 | $compiler->directive('lang', function ($expression) {
233 | return '= Bitrix\Main\Localization\Loc::getMessage('.$expression.') ?>';
234 | });
235 |
236 | $compiler->directive('auth', function () {
237 | return 'IsAuthorized()): ?>';
238 | });
239 | $compiler->directive('guest', function () {
240 | return 'IsAuthorized()): ?>';
241 | });
242 | $compiler->directive('admin', function () {
243 | return 'IsAdmin()): ?>';
244 | });
245 | $compiler->directive('csrf', function ($name = 'sessid') {
246 | $name = !empty($name) ? $name : 'sessid';
247 | $name = trim($name, '"');
248 | $name = trim($name, "'");
249 | return '';
250 | });
251 |
252 | $compiler->directive('endauth', $endIf);
253 | $compiler->directive('endguest', $endIf);
254 | $compiler->directive('endadmin', $endIf);
255 |
256 | static::registerHermitageDirectives($compiler);
257 | }
258 |
259 | /**
260 | * @param BladeCompiler $compiler
261 | */
262 | private static function registerHermitageDirectives($compiler)
263 | {
264 | $simpleDirectives = [
265 | 'actionAddForIBlock' => 'addForIBlock',
266 | ];
267 | foreach ($simpleDirectives as $directive => $action) {
268 | $compiler->directive($directive, function ($expression) use ($action) {
269 | $expression = rtrim($expression, ')');
270 | $expression = ltrim($expression, '(');
271 | return '';
272 | });
273 | }
274 |
275 | $echoDirectives = [
276 | 'actionEditIBlockElement' => 'editIBlockElement',
277 | 'actionDeleteIBlockElement' => 'deleteIBlockElement',
278 | 'actionEditAndDeleteIBlockElement' => 'editAndDeleteIBlockElement',
279 |
280 | 'actionEditIBlockSection' => 'editIBlockSection',
281 | 'actionDeleteIBlockSection' => 'deleteIBlockSection',
282 | 'actionEditAndDeleteIBlockSection' => 'editAndDeleteIBlockSection',
283 |
284 | 'actionEditHLBlockElement' => 'editHLBlockElement',
285 | 'actionDeleteHLBlockElement' => 'deleteHLBlockElement',
286 | 'actionEditAndDeleteHLBlockElement' => 'editAndDeleteHLBlockElement',
287 | ];
288 | foreach ($echoDirectives as $directive => $action) {
289 | $compiler->directive($directive, function ($expression) use ($action) {
290 | $expression = rtrim($expression, ')');
291 | $expression = ltrim($expression, '(');
292 | return '= \Arrilot\BitrixHermitage\Action::' . $action . '($template, ' . $expression . '); ?>';
293 | });
294 | }
295 | }
296 |
297 | /**
298 | * @return string|null
299 | */
300 | public static function getCachePath()
301 | {
302 | return static::$cachePath;
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/src/ViewFinder.php:
--------------------------------------------------------------------------------
1 | paths = $paths;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 | __folder);
26 |
27 | global $APPLICATION, $USER;
28 |
29 | echo $view->file($_SERVER['DOCUMENT_ROOT'].$templateFile, compact(
30 | 'arParams',
31 | 'arResult',
32 | 'arLangMessages',
33 | 'template',
34 | 'templateFolder',
35 | 'parentTemplateFolder',
36 | 'APPLICATION',
37 | 'USER'
38 | ))->render();
39 |
40 | $epilogue = $templateFolder.'/component_epilog.php';
41 | if (file_exists($_SERVER['DOCUMENT_ROOT'].$epilogue)) {
42 | $component = $template->__component;
43 | $component->SetTemplateEpilog([
44 | 'epilogFile' => $epilogue,
45 | 'templateName' => $template->__name,
46 | 'templateFile' => $template->__file,
47 | 'templateFolder' => $template->__folder,
48 | 'templateData' => false,
49 | ]);
50 | }
51 |
52 | BladeProvider::removeTemplateFolderFromViewPaths($template->__folder);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------