云客Drupal源码分析之块系统block

本文深入剖析了Drupal8中的块系统Block,解释了其如何让页面展示多样化,以及块如何与主内容结合形成完整的页面。通过控制器返回的渲染数组,详细介绍了块的配置、显示变体选择、页面构建过程,以及块插件、块实体的管理和交互。此外,还探讨了自定义显示变体、块插件的实现和配置,以及块的访问控制和权限检查机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在drupal中系统流程指向一个控制器,通常控制器返回一个代表特定内容的渲染数组,那么还需要其他内容怎么办?这就是块系统要解决的,她让页面精彩纷呈,可展示多种信息或工具,如果没有她页面会非常单调,某种程度上说她是系统必须的,给各模块展示信息提供页面窗口。

从控制器返回的渲染数组说起:
一个渲染数组可以代表页面中的一部分,也可以是整个页面,在drupal中大多数时候控制器返回的渲染数组代表页面的一部分,这部分是请求的核心目标信息,被称为主内容main content,打开页面主要就是为了得到这个信息,在没有安装块block模块的情况下,页面只显示该信息,如果安装了块block模块,那么块模块会在主内容周围环绕其他信息,比如侧边栏、菜单栏、搜索栏等等;块模块将页面视为由多个区构成(区由主题来划分),这称为分区regions,每个分区中可以放置0个或多个块,每个块呈现一块信息,主内容一般放在主内容区中,要显示哪些信息块、怎么显示以及放在哪个区中显示是可以配置的,可在管理后台的区块配置(/admin/structure/block)中进行,这样就有了丰富多彩的页面了。
以上是宏观上的机制原理,在具体实现上当控制器返回渲染数组后,判断是否是一个局部信息(“#type”不为“page”),如果是那么将其作为主内容,然后派发“选择页面显示变体”事件,如果没有安装块模块,那么使用简单页面显示变体“simple_page”,此时只显示主内容,如果安装了块模块,那么将使用她提供的块页面显示变体“block_page”,该变体接收控制器返回的主内容渲染数组,然后将其和各种块内容组装为一个整页渲染数组(“#type”为“page”)并返回,此时已经得到整个页面的内容了,后续系统将继续执行占位替换、资源排序加载等等工作。
如果控制器直接返回了整页渲染数组,那么系统将跳过块模块的工作,直接继续后面的工作,那么控制器如何返回整页渲染数组呢?首先需要指定“#type”属性的值为“page”,其余部分可以是子元素(每个子元素对应一个分区的渲染数组,不必全部分区都要存在),或者可以是一个主题钩子,此时将钩子对应的模板内容渲染后作为整页内容,如果指定了钩子,那么代表分区渲染数组的子元素将失效,因此这两者是互斥的,除非在模板中使用了这些子元素(将整个数组作为上下文传递到模板中,并在模板中渲染了这些子元素)。
在块block模块中对各类信息块一直是操作的渲染数组,并不将其渲染成最终的html字符串,该渲染工作将在渲染整页渲染数组时在twig模板中进行(在模板中打印一个变量时,如果变量是数组那么将其当做渲染数组进行渲染输出,详见twig服务)

“选择页面显示变体”事件:
只要控制器返回的渲染数组不是整页渲染数组,那么html渲染器(服务id:main_content_renderer.html)将派发“选择页面显示变体”事件:render.page_display_variant.select,默认使用“simple_page”显示变体,但只要块block模块被安装了则将订阅她并无条件设置页面使用“block_page”显示变体
订阅器服务id:block.page_display_variant_subscriber
类:\Drupal\block\EventSubscriber\BlockPageDisplayVariantSubscriber

显示变体插件管理器:
页面显示变体是由显示变体插件管理器管理并实例化的:
服务id:plugin.manager.display_variant
类:Drupal\Core\Display\VariantManager
该插件管理器很简单,插件定义数据的修改钩子为“display_variant_plugin”,定义数据被缓存在“cache.discovery”缓存后端中。

自定义显示变体:
在模块的src/Plugin/DisplayVariant目录下,建立插件类,实现以下接口:
Drupal\Core\Display\VariantInterface
通常继承以下基类:
\Drupal\Core\Display\VariantBase
给出插件释文,清除缓存后插件将被自动收集
系统默认提供的简单页面变体是一个很好的参考:
\Drupal\Core\Render\Plugin\DisplayVariant\SimplePageVariant
请见该插件的实现

“block_page”显示变体:
这是块模块参与页面渲染流程的入口,插件id:block_page,插件类定义:
\Drupal\block\Plugin\DisplayVariant\BlockPageVariant
由于她实现了容器工厂插件接口,所以在插件管理器中将通过她的create静态方法来实例化(见本系列插件篇下集)
其build()方法返回整页渲染数组,每个子元素对应一个页面分区的渲染数组,以分区机器名作为子元素名,这里将其称为分区渲染数组,每个分区渲染数组包含1个或多子元素,每个子元素对应一个块的渲染数组;相反的,如果分区内一个块也没有,该整页渲染数组将不会包含该分区渲染数组。
在理解该显示变体的工作逻辑前,我们需要先了解块系统。

块系统概述:
一个drupal页面是由多个块构成的,每个块提供一块信息,通常主要区域显示控制器返回的主内容,该区域叫做主内容块,其周边分布着其他块,所有的块由块系统管理,块系统主要由块插件和块实体两大部分构成,块插件用于构建块的内容,块实体属于配置实体,用于提供前者的配置数据,如显示条件、分区位置、插件参数等,这两者有机结合形成了块系统,每一个块插件(类)可以根据不同的配置实例化出对应的多个块(实例对象),每个实例的配置都不同,他们共享相同的初始配置,一旦实例化后各实例有对应的块实体来储存配置信息,因此在后台:管理》结构》区块布局中可以将一个块(对应程序中的块插件)同时放置到多个分区中,每个分区中的块对应程序中的一

<think>嗯,用户想在蓝桥云客的实验环境中通过操作系统调用来创建进程。首先,我需要确定用户使用的编程语言是什么,因为不同的语言有不同的系统调用方法。根据提供的引用内容,特别是引用[3]中的Java代码示例,用户可能更熟悉Java。不过,蓝桥杯比赛中也可能使用C/C++,所以可能需要涵盖这两种情况。 接下来,我需要回忆如何在不同的编程语言中进行系统调用。例如,在C语言中,可以使用`fork()`函数来创建进程,而Windows环境下可能使用`CreateProcess`。不过,蓝桥云客的环境可能基于Linux,所以`fork()`和`exec`系列函数更相关。对于Java,虽然标准库中的`ProcessBuilder`或`Runtime.exec()`可以启动新进程,但这是更高层次的抽象,不是直接的系统调用。 然后,需要考虑用户的实际需求。用户可能希望了解如何在实验中编写代码来创建进程,可能涉及到分叉和执行的示例。需要确保提供的代码示例在蓝桥云客环境中可行,并且语法正确。同时,根据用户提供的引用[2]中的排序算法示例,用户可能对代码结构有一定了解,因此示例应简洁明了。 另外,用户的问题中提到“操作系统调用”,所以可能需要区分直接的系统调用(如C中的`fork()`)和高级语言封装后的方法(如Java的`ProcessBuilder`)。需要明确说明这一点,避免混淆。 还要检查是否有相关的引用内容可以支持回答。例如,引用[2]提到排序算法的时间复杂度,虽然与进程创建无关,但可以忽略这部分。引用[3]中的Java代码示例可能对编写Java版本的进程创建有帮助。 最后,生成相关问题时要围绕进程创建的方法、不同语言的实现、蓝桥杯中的实际应用等,确保问题有针对性,帮助用户深入理解。</think>### 如何在蓝桥云客实验环境中通过系统调用创建进程 在蓝桥云客实验环境中,创建进程通常通过操作系统系统调用实现。不同编程语言对系统调用的封装方式不同,以下是两种常见语言的实现方法: --- #### **1. C/C++ 中的 `fork()` 系统调用** 在Linux系统中,`fork()` 是创建进程的核心系统调用。它会复制当前进程(父进程)生成子进程,子进程从 `fork()` 的返回点开始执行。 **示例代码**: ```c #include <stdio.h> #include <unistd.h> int main() { pid_t pid = fork(); // 创建子进程 if (pid < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid == 0) { // 子进程代码 printf("Child Process (PID=%d)\n", getpid()); } else { // 父进程代码 printf("Parent Process (PID=%d), Child PID=%d\n", getpid(), pid); } return 0; } ``` **关键点**: - `fork()` 返回两次:父进程返回子进程PID,子进程返回0。 - 父子进程共享代码段,但拥有独立的数据段和堆栈[^2]。 --- #### **2. Java 中的 `ProcessBuilder` 类** Java通过 `ProcessBuilder` 或 `Runtime.exec()` 封装系统调用,启动外部进程。 **示例代码**(统计文件行数): ```java import java.io.IOException; public class ProcessDemo { public static void main(String[] args) { try { ProcessBuilder pb = new ProcessBuilder("wc", "-l", "/path/to/file.txt"); Process process = pb.start(); int exitCode = process.waitFor(); System.out.println("Exit Code: " + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } ``` **关键点**: - `ProcessBuilder` 提供进程环境配置(如参数、工作目录)[^3]。 - 需处理输入/输出流和异常。 --- #### **蓝桥实验环境注意事项** 1. **权限限制**:部分实验环境可能限制直接系统调用,需遵循实验指导。 2. **语言支持**:C/C++需编译后运行,Java需注意包管理和类路径。 3. **调试技巧**:使用 `ps` 或 `top` 命令观察进程状态(Linux环境)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值