在 Laravel 6 中,运行包含 PHP Blade 语法的文件,进而获取其输出
1、现有一段 PHP Blade 代码块,放在 MySQL 中,其代码存放在字段 schema 中,如图1
2、一些变量,已经在 PHP 脚本中声明,代码块如下所示
{ "sections": { @foreach ($homepage as $id => $section) {{-- 轮播图 --}} @if($section['type'] === 'carousel') "{{ $id }}": { "type": "carousel", "settings": { "loop": true, "autoplay": true, "height": "{{ isset($section['slide_height']) && $section['slide_height'] === 'full' ? 'full' : 'auto' }}", "interval": {{ $section['interval'] }} }, "blocks": { @if (isset($section['items'])) @foreach ($section['items'] as $key => $item) "slide-{{ $key }}": { "type": "slide", "settings": { "image": "{{ @$item['image']['url'] ?: '' }}", "url": "{{ $item['button']['url'] }}", "align": "{{ $item['align'] }}", "title": {-- $item['title'] --}, "description": {-- $item['description'] --}, "button": {-- $item['button']['text'] --}, "opacity": {{ $item['show_overlay'] ? (100 - $item['opacity']) : 100 }}, "button_background_color": "{{ $item['button_background_color'] }}", "button_color": "{{ $item['button_text_color']}}", "color": "{{ $item['text_color'] }}" } }@if (!$loop->last),@endif @endforeach @endif } }, @endif {{-- 商品分类 --}} @if($section['type'] === 'collection') "{{ $id }}": { "type": "main-collections", "settings": { "heading": {-- $section['title'] --} }, "blocks": { @foreach ($section['ids'] as $key => $item) "collection-{{ $key }}": { "type": "collection", "settings": { "id": {{ $item }} } }@if (!$loop->last),@endif @endforeach } }, @endif {{-- 精选商品 --}} @if($section['type'] === 'product') "{{ $id }}": { "type": "main-products", "settings": { "show_product_palette": {{ @Module::find('ProductPalette')->isEnabled() && @config('product-palette.show_position') === 'all' ? 'true' : 'false' }}, "heading": {-- $section['title'] --}, "layout": "{{ $section['layout'] }}", "page_size": {{ $section['amount'] }} }, "blocks": { @foreach ($section['category'] as $key => $item) "collection-{{ $key }}": { "type": "collection", "settings": { "id": {{ $item }} } }@if (!$loop->last),@endif @endforeach } }, @endif {{-- 邮件订阅 --}} @if($section['type'] === 'subscribe') "{{ $id }}": { "type": "apps", "blocks": { "newsletter": { "type": "object/Newsletter/blocks/newsletter", "settings": { "heading": {-- $section['title'] --}, "description": {-- $section['description'] --} } } } }, @endif {{-- 图片 --}} @if($section['type'] === 'image') "{{ $id }}": { "type": "image", "settings": { "heading": {-- $section['title'] --}, "heading_align": "{{ $section['align'] }}" }, "blocks": { @if (isset($section['items'])) @foreach ($section['items'] as $key => $item) "column-{{ $key }}": { "type": "column", "settings": { "image": "{{ @$item['image']['url'] ?: '' }}", "mobile_image": "{{ @$item['mb_image']['url'] ?: '' }}", "url": "{{ $item['url'] }}", "title": "{{ @$item['url_object']['title'] ?: '' }}" } }@if (!$loop->last),@endif @endforeach @endif } }, @endif {{-- 图文 --}} @if($section['type'] === 'imagetext') {{-- 网格 --}} @if($section['layout_type'] === 'grid') "{{ $id }}": { "type": "multi-column", "settings": { "heading": {-- $section['title_text'] ?: '' --}, "heading_align": "{{ $section['title_aligns'] }}", "text_color": "{{ $section['text_color_v2'] }}", "image_size": "{{ $section['image_style'] === 'auto' ? 'auto' : 'fixed' }}", "mask_opacity": {{ $section['opacity'] }}, "button_text_color": "{{ $section['button_text_color_v2'] }}", "button_background_color": "{{ @$section['button_bg_color_v2'] ?: '' }}", "text_align": "{{ $section['align'] }}" }, "blocks": { @if (isset($section['items'])) @foreach ($section['items'] as $key => $item) "column-{{ $key }}": { "type": "column", "settings": { "heading": {-- $item['title'] ?: '' --}, "text": {-- @$item['description'] ?:'' --}, "image": "{{ @$item['image']['url'] ?: '' }}", "mobile_image": "{{ @$item['mobile_image']['url'] ?: '' }}", "url": "{{ @$item['button']['url_object']['url'] }}", "label": {-- $item['button']['text'] --} } }@if (!$loop->last),@endif @endforeach @endif } }, @else {{-- 左文右图(left) --}} {{-- 左图右文(right) --}} "{{ $id }}": { "type": "image-text", "settings": { "heading": {-- $section['title_text'] --}, "heading_align": "{{ $section['title_aligns'] }}", "image": "{{ @$section['horizontal']['image']['url'] }}", "image_placement": "{{ $section['layout_type'] === 'left' ? 'second' : 'first' }}", "horizontal_align": "{{ $section['horizontal']['level_align'] }}", "vertical_align": "{{ $section['horizontal']['vertical_align'] === 'flex-start' ? 'top' : ($section['horizontal']['vertical_align'] === 'flex-end' ? 'bottom' : 'middle') }}", "text_color": "{{ $section['text_color_v2'] }}", "background_color": "{{ @$section['text_bg_color_v2'] ?: '' }}" }, "blocks": { "heading": { "type": "heading", "settings": { "heading": {-- $section['horizontal']['title'] --} } }, "text": { "type": "text", "settings": { "text": {-- $section['horizontal']['description'] --} } }, "button": { "type": "button", "settings": { "url": "{{ @$section['horizontal']['button']['url_object']['url'] }}", "label": {-- $section['horizontal']['button']['text'] --}, "text_color": "{{ $section['button_text_color_v2'] }}", "background_color": "{{ $section['button_bg_color_v2'] }}" } } } }, @endif @endif @endforeach "0": { "type": "empty" } } }
3、编译后的模板文件代码如下所示。
{ "sections": { <?php $__currentLoopData = $homepage; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $id => $section): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> <?php if($section['type'] === 'carousel'): ?> "<?php echo e($id); ?>": { "type": "carousel", "settings": { "loop": true, "autoplay": true, "height": "<?php echo e(isset($section['slide_height']) && $section['slide_height'] === 'full' ? 'full' : 'auto'); ?>", "interval": <?php echo e($section['interval']); ?> }, "blocks": { <?php if(isset($section['items'])): ?> <?php $__currentLoopData = $section['items']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> "slide-<?php echo e($key); ?>": { "type": "slide", "settings": { "image": "<?php echo e(@$item['image']['url'] ?: ''); ?>", "url": "<?php echo e($item['button']['url']); ?>", "align": "<?php echo e($item['align']); ?>", "title": <?php echo json_encode($item['title'], JSON_UNESCAPED_UNICODE); ?>, "description": <?php echo json_encode($item['description'], JSON_UNESCAPED_UNICODE); ?>, "button": <?php echo json_encode($item['button']['text'], JSON_UNESCAPED_UNICODE); ?>, "opacity": <?php echo e($item['show_overlay'] ? (100 - $item['opacity']) : 100); ?>, "button_background_color": "<?php echo e($item['button_background_color']); ?>", "button_color": "<?php echo e($item['button_text_color']); ?>", "color": "<?php echo e($item['text_color']); ?>" } }<?php if(!$loop->last): ?>,<?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> <?php endif; ?> } }, <?php endif; ?> <?php if($section['type'] === 'collection'): ?> "<?php echo e($id); ?>": { "type": "main-collections", "settings": { "heading": <?php echo json_encode($section['title'], JSON_UNESCAPED_UNICODE); ?> }, "blocks": { <?php $__currentLoopData = $section['ids']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> "collection-<?php echo e($key); ?>": { "type": "collection", "settings": { "id": <?php echo e($item); ?> } }<?php if(!$loop->last): ?>,<?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> } }, <?php endif; ?> <?php if($section['type'] === 'product'): ?> "<?php echo e($id); ?>": { "type": "main-products", "settings": { "show_product_palette": <?php echo e(@Module::find('ProductPalette')->isEnabled() && @config('product-palette.show_position') === 'all' ? 'true' : 'false'); ?>, "heading": <?php echo json_encode($section['title'], JSON_UNESCAPED_UNICODE); ?>, "layout": "<?php echo e($section['layout']); ?>", "page_size": <?php echo e($section['amount']); ?> }, "blocks": { <?php $__currentLoopData = $section['category']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> "collection-<?php echo e($key); ?>": { "type": "collection", "settings": { "id": <?php echo e($item); ?> } }<?php if(!$loop->last): ?>,<?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> } }, <?php endif; ?> <?php if($section['type'] === 'subscribe'): ?> "<?php echo e($id); ?>": { "type": "apps", "blocks": { "newsletter": { "type": "object/Newsletter/blocks/newsletter", "settings": { "heading": <?php echo json_encode($section['title'], JSON_UNESCAPED_UNICODE); ?>, "description": <?php echo json_encode($section['description'], JSON_UNESCAPED_UNICODE); ?> } } } }, <?php endif; ?> <?php if($section['type'] === 'image'): ?> "<?php echo e($id); ?>": { "type": "image", "settings": { "heading": <?php echo json_encode($section['title'], JSON_UNESCAPED_UNICODE); ?>, "heading_align": "<?php echo e($section['align']); ?>" }, "blocks": { <?php if(isset($section['items'])): ?> <?php $__currentLoopData = $section['items']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> "column-<?php echo e($key); ?>": { "type": "column", "settings": { "image": "<?php echo e(@$item['image']['url'] ?: ''); ?>", "mobile_image": "<?php echo e(@$item['mb_image']['url'] ?: ''); ?>", "url": "<?php echo e($item['url']); ?>", "title": "<?php echo e(@$item['url_object']['title'] ?: ''); ?>" } }<?php if(!$loop->last): ?>,<?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> <?php endif; ?> } }, <?php endif; ?> <?php if($section['type'] === 'imagetext'): ?> <?php if($section['layout_type'] === 'grid'): ?> "<?php echo e($id); ?>": { "type": "multi-column", "settings": { "heading": <?php echo json_encode($section['title_text'] ?: '', JSON_UNESCAPED_UNICODE); ?>, "heading_align": "<?php echo e($section['title_aligns']); ?>", "text_color": "<?php echo e($section['text_color_v2']); ?>", "image_size": "<?php echo e($section['image_style'] === 'auto' ? 'auto' : 'fixed'); ?>", "mask_opacity": <?php echo e($section['opacity']); ?>, "button_text_color": "<?php echo e($section['button_text_color_v2']); ?>", "button_background_color": "<?php echo e(@$section['button_bg_color_v2'] ?: ''); ?>", "text_align": "<?php echo e($section['align']); ?>" }, "blocks": { <?php if(isset($section['items'])): ?> <?php $__currentLoopData = $section['items']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> "column-<?php echo e($key); ?>": { "type": "column", "settings": { "heading": <?php echo json_encode($item['title'] ?: '', JSON_UNESCAPED_UNICODE); ?>, "text": <?php echo json_encode(@$item['description'] ?:'', JSON_UNESCAPED_UNICODE); ?>, "image": "<?php echo e(@$item['image']['url'] ?: ''); ?>", "mobile_image": "<?php echo e(@$item['mobile_image']['url'] ?: ''); ?>", "url": "<?php echo e(@$item['button']['url_object']['url']); ?>", "label": <?php echo json_encode($item['button']['text'], JSON_UNESCAPED_UNICODE); ?> } }<?php if(!$loop->last): ?>,<?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> <?php endif; ?> } }, <?php else: ?> "<?php echo e($id); ?>": { "type": "image-text", "settings": { "heading": <?php echo json_encode($section['title_text'], JSON_UNESCAPED_UNICODE); ?>, "heading_align": "<?php echo e($section['title_aligns']); ?>", "image": "<?php echo e(@$section['horizontal']['image']['url']); ?>", "image_placement": "<?php echo e($section['layout_type'] === 'left' ? 'second' : 'first'); ?>", "horizontal_align": "<?php echo e($section['horizontal']['level_align']); ?>", "vertical_align": "<?php echo e($section['horizontal']['vertical_align'] === 'flex-start' ? 'top' : ($section['horizontal']['vertical_align'] === 'flex-end' ? 'bottom' : 'middle')); ?>", "text_color": "<?php echo e($section['text_color_v2']); ?>", "background_color": "<?php echo e(@$section['text_bg_color_v2'] ?: ''); ?>" }, "blocks": { "heading": { "type": "heading", "settings": { "heading": <?php echo json_encode($section['horizontal']['title'], JSON_UNESCAPED_UNICODE); ?> } }, "text": { "type": "text", "settings": { "text": <?php echo json_encode($section['horizontal']['description'], JSON_UNESCAPED_UNICODE); ?> } }, "button": { "type": "button", "settings": { "url": "<?php echo e(@$section['horizontal']['button']['url_object']['url']); ?>", "label": <?php echo json_encode($section['horizontal']['button']['text'], JSON_UNESCAPED_UNICODE); ?>, "text_color": "<?php echo e($section['button_text_color_v2']); ?>", "background_color": "<?php echo e($section['button_bg_color_v2']); ?>" } } } }, <?php endif; ?> <?php endif; ?> <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?> "0": { "type": "empty" } } }
4、决定跳过将 PHP Blade 文件编译为 PHP 模板文件的这一步,而是手动调整为编译后的格式,然后去除掉其中的 Blade 语法。以下为编译后的一段 Blade 格式示例。如图2
5、以下为去除掉其中的 Blade 语法后的示例。涉及到像 $__env 的变量已经全部删除。如图3
6、决定先尝试编写一个简单的代码块,以验证技术方案的可行性。如果可行,再尝试执行步骤 5 所生成的代码块。
{ <?php $items = [1, 2, 3]; ?> <?php if(isset($items)): ?> <?php $count = count($items); foreach($items as $key => $item): $count--; ?> "slide-<?php echo $key; ?>": { "type": "slide", "settings": { "image": "<?php echo $item ?: ''; ?>" } }<?php if($count != 0): ?>,<?php endif; ?> <?php endforeach; ?> <?php endif; ?> }
7、直接运行此代码所在的文件(phpcode.php),其结果转换为 json 结构,符合预期。如图4
{ "slide-0": { "type": "slide", "settings": { "image": "1" } }, "slide-1": { "type": "slide", "settings": { "image": "2" } }, "slide-2": { "type": "slide", "settings": { "image": "3" } } }
8、现在需要新建一个 PHP 脚本,然后使用输出缓冲来将 PHP 文件包含入一个字符串。参考:https://www.php.net/manual/zh/function.include.php ,新建 PHP 文件 ob.php,运行后,输出 json 结构,符合预期。如图5
<?php $string = get_include_contents('phpcode.php'); echo $string; function get_include_contents($filename) { if (is_file($filename)) { ob_start(); include $filename; $contents = ob_get_contents(); ob_end_clean(); return $contents; } return false; } ?>
9、不过由于前端同事认为这样调整模板为 PHP 原生语法的方案可读性差,工作量过大。仍然要求后端自动完成所有的流程,也包含 Blade 编译为 PHP 模板的流程。最终实现方案如下
/* $filename = $directory . '/' . substr($themeAsset->asset_key, 6); if (Storage::disk('local')->exists($filename)) { ob_start(); include storage_path('app') . '/' . $filename; $contents = ob_get_contents(); ob_end_clean(); $themeAsset->schema = $contents; $themeAsset->save(); } */ View::addLocation(storage_path('app') . '/' . $directory); $filename = substr(substr($originalThemeAsset->asset_key, 0, strpos($originalThemeAsset->asset_key, '.')), 6); $variables = $this->getBladeSchemaCompilerVariables($filename, $originalThemeInstallation->theme); $schema = View::make($filename)->with($variables)->render(); $themeAsset->schema = $schema; $themeAsset->save();
近期评论