在 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();





近期评论