- Symfony 教程
- Symfony - 首页
- Symfony - 简介
- Symfony - 安装
- Symfony - 架构
- Symfony - 组件
- Symfony - 服务容器
- Symfony - 事件 & 事件监听器
- Symfony - 表达式
- Symfony - 捆绑包
- 创建简单的 Web 应用
- Symfony - 控制器
- Symfony - 路由
- Symfony - 视图引擎
- Symfony - Doctrine ORM
- Symfony - 表单
- Symfony - 验证
- Symfony - 文件上传
- Symfony - Ajax 控制
- Cookie & 会话管理
- Symfony - 国际化
- Symfony - 日志记录
- Symfony - 邮件管理
- Symfony - 单元测试
- Symfony - 高级概念
- Symfony - REST 版本
- Symfony - CMF 版本
- 完整的运行示例
- Symfony 有用资源
- Symfony - 快速指南
- Symfony - 有用资源
- Symfony - 讨论
Symfony - 高级概念
在本章中,我们将学习 Symfony 框架中的一些高级概念。
HTTP 缓存
在 Web 应用中缓存可以提高性能。例如,购物车 Web 应用中的热销产品可以缓存一段时间,以便能够以快速的方式呈现给客户,而无需访问数据库。以下是缓存的一些基本组件。
缓存项
缓存项是作为键值对存储的单个信息单元。键应该是字符串,值可以是任何 PHP 对象。PHP 对象通过序列化存储为字符串,并在读取项时转换回对象。
缓存适配器
缓存适配器是将项存储在存储中的实际机制。存储可以是内存、文件系统、数据库、Redis 等。缓存组件提供了一个AdapterInterface,通过它适配器可以将缓存项存储在后端存储中。有很多内置的缓存适配器可用。其中一些如下所示:
数组缓存适配器 - 缓存项存储在 PHP 数组中。
文件系统缓存适配器 - 缓存项存储在文件中。
PHP 文件缓存适配器 - 缓存项存储为 php 文件。
APCu 缓存适配器 - 缓存项使用 PHP APCu 扩展存储在共享内存中。
Redis 缓存适配器 - 缓存项存储在 Redis 服务器中。
PDO 和 Doctrine DBAL 缓存适配器 - 缓存项存储在数据库中。
链式缓存适配器 - 为复制目的组合多个缓存适配器。
代理缓存适配器 - 缓存项使用实现 CacheItemPoolInterface 的第三方适配器存储。
缓存池
缓存池是缓存项的逻辑存储库。缓存池由缓存适配器实现。
简单应用
让我们创建一个简单的应用来理解缓存的概念。
步骤 1 - 创建一个新的应用,cache-example。
cd /path/to/app mkdir cache-example cd cache-example
步骤 2 - 安装缓存组件。
composer require symfony/cache
步骤 3 - 创建一个文件系统适配器。
require __DIR__ . '/vendor/autoload.php'; use Symfony\Component\Cache\Adapter\FilesystemAdapter; $cache = new FilesystemAdapter();
步骤 4 - 使用适配器的getItem和set方法创建一个缓存项。getItem 使用其键获取缓存项。如果键不存在,则创建一个新项。set 方法存储实际数据。
$usercache = $cache->getitem('item.users'); $usercache->set(['jon', 'peter']); $cache->save($usercache);
步骤 5 - 使用getItem、isHit和get方法访问缓存项。isHit 通知缓存项的可用性,get 方法提供实际数据。
$userCache = $cache->getItem('item.users'); if(!$userCache->isHit()) { echo "item.users is not available"; } else { $users = $userCache->get(); var_dump($users); }
步骤 6 - 使用deleteItem方法删除缓存项。
$cache->deleteItem('item.users');
完整的代码清单如下所示。
<?php require __DIR__ . '/vendor/autoload.php'; use Symfony\Component\Cache\Adapter\FilesystemAdapter; $cache = new FilesystemAdapter(); $usercache = $cache->getitem('item.users'); $usercache->set(['jon', 'peter']); $cache->save($usercache); $userCache = $cache->getItem('item.users'); if(!$userCache->isHit()) { echo "item.users is not available"; } else { $users = $userCache->get(); var_dump($users); } $cache->deleteItem('item.users'); ?>
结果
array(2) { [0]=> string(3) "jon" [1]=> string(5) "peter" }
调试
调试是在开发应用过程中最常见的活动之一。Symfony 提供了一个单独的组件来简化调试过程。我们可以通过调用 Debug 类的enable方法来启用 Symfony 调试工具。
use Symfony\Component\Debug\Debug Debug::enable()
Symfony 提供了两个类,ErrorHandler和ExceptionHandler用于调试目的。ErrorHandler 捕获 PHP 错误并将其转换为异常(ErrorException 或 FatalErrorException),ExceptionHandler 捕获未捕获的 PHP 异常并将其转换为有用的 PHP 响应。ErrorHandler 和 ExceptionHandler 默认情况下是禁用的。我们可以使用 register 方法启用它。
use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; ErrorHandler::register(); ExceptionHandler::register();
在 Symfony Web 应用中,调试环境由 DebugBundle 提供。在 AppKernel 的registerBundles方法中注册捆绑包以启用它。
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); }
探查器
应用的开发需要一个世界级的探查工具。探查工具收集应用的所有运行时信息,例如执行时间、各个模块的执行时间、数据库活动花费的时间、内存使用情况等。Web 应用除了上述指标之外,还需要更多信息,例如请求时间、创建响应花费的时间等。
Symfony 默认情况下在 Web 应用中启用了所有这些信息。Symfony 为 Web 探查提供了一个单独的捆绑包,称为WebProfilerBundle。可以通过在 AppKernel 的 registerBundles 方法中注册捆绑包来在 Web 应用中启用 Web 探查器捆绑包。
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); }
Web 探查组件可以在应用配置文件app/config/config.xml的web_profile 部分中进行配置。
web_profiler: toolbar: false position: bottom
Symfony 应用在页面底部显示探查数据作为单独的部分。
Symfony 还提供了一种简单的方法,可以使用DataCollectorInterface 接口和 twig 模板在探查数据中添加有关页面的自定义详细信息。简而言之,Symfony 通过提供一个相对简单的世界级探查框架,使 Web 开发人员能够轻松地创建世界级的应用。
安全
如前所述,Symfony 通过其安全组件提供了一个强大的安全框架。安全组件分为以下四个子组件。
- symfony/security-core - 核心安全功能。
- symfony/security-http - HTTP 协议中集成的安全功能。
- symfony/security-csrf - 防御 Web 应用中的跨站点请求伪造。
- symfony/security-acl - 基于高级访问控制列表的安全框架。
简单的身份验证和授权
让我们使用一个简单的演示应用学习身份验证和授权的概念。
步骤 1 - 使用以下命令创建一个新的 Web 应用securitydemo。
symfony new securitydemo
步骤 2 - 使用安全配置文件在应用中启用安全功能。安全相关的配置放置在单独的文件security.yml中。默认配置如下所示。
security: providers: in_memory: memory: ~ firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: anonymous: ~ #http_basic: ~ #form_login: ~
默认配置启用了基于内存的安全提供程序和对所有页面的匿名访问。防火墙部分从安全框架中排除了与模式^/(_(profiler|wdt)|css|images|js)/匹配的文件。默认模式包括样式表、图像和 JavaScript(以及调试工具,如探查器)。
步骤 3 - 通过在 main 部分添加 http_basic 选项来启用基于 HTTP 的安全身份验证系统,如下所示。
security: # ... firewalls: # ... main: anonymous: ~ http_basic: ~ #form_login: ~
步骤 4 - 在内存提供程序部分添加一些用户。此外,为用户添加角色。
security: providers: in_memory: memory: users: myuser: password: user roles: 'ROLE_USER' myadmin: password: admin roles: 'ROLE_ADMIN'
我们添加了两个用户,user的角色为 ROLE_USER,admin的角色为 ROLE_ADMIN。
步骤 5 - 添加编码器以获取当前登录用户的完整详细信息。编码器的目的是从 Web 请求中获取当前用户对象的完整详细信息。
security: # ... encoders: Symfony\Component\Security\Core\User\User: bcrypt # ...
Symfony 提供了一个接口UserInterface来获取用户详细信息,例如用户名、角色、密码等。我们需要根据我们的需求实现该接口并在编码器部分进行配置。
例如,让我们假设用户详细信息在数据库中。然后,我们需要创建一个新的 User 类并实现 UserInterface 方法以从数据库中获取用户详细信息。一旦数据可用,安全系统就会使用它来允许/拒绝用户。Symfony 为内存提供程序提供了一个默认的用户实现。算法用于解密用户密码。
步骤 6 - 使用bcrypt算法加密用户密码并将其放置在配置文件中。由于我们使用了bcrypt算法,因此 User 对象会尝试解密配置文件中指定的密码,然后尝试与用户输入的密码匹配。Symfony 控制台应用提供了一个简单的命令来加密密码。
php bin/console security:encode-password admin Symfony Password Encoder Utility ================================ ------------------ ----------------------------------- Key Value ------------------ ------------------------------------ Encoder used Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder Encoded password $2y$12$0Hy6/.MNxWdFcCRDdstHU.hT5j3Mg1tqBunMLIUYkz6..IucpaPNO ------------------ ------------------------------------ ! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt. [OK] Password encoding succeeded
步骤 7 - 使用该命令生成加密的密码并将其更新到配置文件中。
# To get started with security, check out the documentation: # http://symfony.com/doc/current/security.html security: # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded providers: in_memory: memory: users: user: password: $2y$13$WsGWNufreEnVK1InBXL2cO/U7WftvfNvH Vb/IJBH6JiYoDwVN4zoi roles: 'ROLE_USER' admin: password: $2y$13$jQNdIeoNV1BKVbpnBuhKRuOL01NeMK F7nEqEi/Mqlzgts0njK3toy roles: 'ROLE_ADMIN' encoders: Symfony\Component\Security\Core\User\User: bcrypt firewalls: # disables authentication for assets and the profiler, # adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: anonymous: ~ # activate different ways to authenticate # http://symfony.com/doc/current/security.html#a-co nfiguring-howyour-users-will-authenticate http_basic: ~ # http://symfony.com/doc/current/cookbook/security/ form_login_setup.html #form_login: ~
步骤 8 - 现在,将安全应用于应用的某些部分。例如,将管理员部分限制为具有 ROLE_ADMIN 角色的用户。
security: # ... firewalls: # ... default: # ... access_control: # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: 'ROLE_ADMIN' }
步骤 9 - 在 DefaultController 中添加一个管理员页面,如下所示。
/** * @Route("/admin") */ public function adminLandingAction() { return new Response('<html><body>This is admin section.</body></html>'); }
步骤 10 - 最后,在浏览器中访问管理员页面以检查安全配置。浏览器将要求输入用户名和密码,并且只允许已配置的用户。
结果
工作流
工作流是一个高级概念,在许多企业应用中都有使用。在电子商务应用中,产品交付流程就是一个工作流。产品首先计费(创建订单),从商店采购并包装(包装/准备发货),然后发货给用户。如果出现任何问题,产品将从用户处退回,并且订单将被撤销。操作流程的顺序非常重要。例如,在没有计费的情况下,我们无法交付产品。
Symfony 组件提供了一种面向对象的方式来定义和管理工作流。流程中的每个步骤称为位置,从一个位置移动到另一个位置所需的动作称为转换。创建工作流的位置和转换的集合称为工作流定义。
让我们通过为请假管理创建一个简单的应用来理解工作流的概念。
步骤 1 - 创建一个新的应用,workflow-example。
cd /path/to/dev mkdir workflow-example cd workflow-example composer require symfony/workflow
步骤 2 - 创建一个新类Leave,其中包含applied_by、leave_on和status属性。
class Leave { public $applied_by; public $leave_on; public $status; }
这里,applied_by 指的是想要请假的员工。leave_on 指的是请假日期。status 指的是请假状态。
步骤 3 - 请假管理有四个位置,applied、in_process 和 approved/rejected。
use Symfony\Component\Workflow\DefinitionBuilder; use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Workflow; use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Dumper\GraphvizDumper; $builder = new DefinitionBuilder(); $builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']);
在这里,我们使用DefinitionBuilder创建了一个新的定义,并使用addPlaces方法添加了位置。
步骤 4 - 定义从一个位置移动到另一个位置所需的动作。
$builder->addTransition(new Transition('to_process', 'applied', 'in_process')); $builder->addTransition(new Transition('approve', 'in_process', 'approved')); $builder->addTransition(new Transition('reject', 'in_process', 'rejected'));
在这里,我们有三个转换,to_process、approve和reject。to_process 转换接受请假申请并将位置从 applied 移动到 in_process。approve 转换批准请假申请并将位置移动到 approved。类似地,reject 转换拒绝请假申请并将位置移动到 rejected。我们使用 addTransition 方法创建了所有转换。
步骤 5 - 使用 build 方法构建定义。
$definition = $builder->build();
步骤 6 - 可选地,定义可以作为 graphviz dot 格式转储,可以将其转换为图像文件以供参考。
$dumper = new GraphvizDumper(); echo $dumper->dump($definition);
步骤 7 - 创建一个标记存储,用于存储对象的当前位置/状态。
$marking = new SingleStateMarkingStore('status');
在这里,我们使用了SingleStateMarkingStore类来创建标记,它将当前状态标记到对象的 status 属性中。在我们的示例中,对象是 Leave 对象。
步骤 8 - 使用定义和标记创建工作流。
$leaveWorkflow = new Workflow($definition, $marking);
在这里,我们使用了Workflow类来创建工作流。
步骤 9 - 使用Registry类将工作流添加到工作流框架的注册表中。
$registry = new Registry(); $registry->add($leaveWorkflow, Leave::class);
步骤 10 - 最后,使用工作流查找是否使用can方法应用了给定的转换,如果已应用,则使用 apply 方法应用转换。当转换应用时,对象的状态从一个位置移动到另一个位置。
$workflow = $registry->get($leave); echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; $workflow->apply($leave, 'to_process'); echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; echo $leave->status . "\r\n"; $workflow->apply($leave, 'approve'); echo $leave->status . "\r\n";
完整的代码如下所示:
<?php require __DIR__ . '/vendor/autoload.php'; use Symfony\Component\Workflow\DefinitionBuilder; use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Workflow; use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Dumper\GraphvizDumper; class Leave { public $applied_by; public $leave_on; public $status; } $builder = new DefinitionBuilder(); $builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']); $builder->addTransition(new Transition('to_process', 'applied', 'in_process')); $builder->addTransition(new Transition('approve', 'in_process', 'approved')); $builder->addTransition(new Transition('reject', 'in_process', 'rejected')); $definition = $builder->build(); // $dumper = new GraphvizDumper(); // echo $dumper->dump($definition); $marking = new SingleStateMarkingStore('status'); $leaveWorkflow = new Workflow($definition, $marking); $registry = new Registry(); $registry->add($leaveWorkflow, Leave::class); $leave = new Leave(); $leave->applied_by = "Jon"; $leave->leave_on = "1998-12-12"; $leave->status = 'applied'; $workflow = $registry->get($leave); echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; $workflow->apply($leave, 'to_process'); echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; echo $leave->status . "\r\n"; $workflow->apply($leave, 'approve'); echo $leave->status . "\r\n"; ?>
结果
Can we approve the leave now? Can we approve the start process now? 1 Can we approve the leave now? 1 in_process approved