Symfony高级概念 Symfony单元测试 Symfony REST版 在本章中,我们将学习Symfony框架中的一些高级概念。 HTTP缓存 在Web应用程序中缓存可提高性能。例如,购物车Web应用程序中的热门产品可以在有限的时间内进行缓存,以便可以在不触及数据库的情况下以快速的方式将其呈现给客户。以下是Cache的一些基本组件。 缓存项目 缓存项目是存储为键/值对的单个信息单元。在 关键的 应该是字符串, 值 可以是任何PHP对象。PHP对象通过序列化以字符串形式存储,并在读取项目时将其转换回对象。 缓存适配器 缓存适配器是将商品存储在商店中的实际机制。存储可以是内存,文件系统,数据库, Redis 等。高速缓存组件提供适配器接口,适配器可以通过该适配器将高速缓存项目存储在后端存储中。有很多内置的缓存适配器可用。其中几个如下 - 数组缓存适配器 - 缓存项目存储在PHP数组中。 文件系统缓存适配器 - 缓存项目存储在文件中。 PHP文件缓存适配器 - 缓存项目存储为php文件。 APCu缓存适配器 - 缓存项目使用PHP APCu扩展存储在共享内存中。 Redis缓存适配器 - 缓存项目存储在Redis服务器中。 PDO和Doctrine DBAL缓存适配器 - 缓存项目存储在数据库中。 链高速缓存适配器 - 组合多个高速缓存适配器用于复制目的。 代理缓存适配器 - 缓存项目使用实现CacheItemPoolInterface的第三方适配器进行存储。 缓存池 缓存池是缓存项目的逻辑存储库。缓存池由缓存适配器实现。 简单的应用 让我们创建一个简单的应用程序来理解缓存概念。 第1步 - 创建一个新的应用程序, 缓存示例 。 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 和适配器的 设置 方法创建缓存项目。getItem使用其键获取缓存项。如果密钥不是persent,它会创建一个新项目。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默认是禁用的。我们可以通过使用注册方法来启用它。 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 Profiler包。 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接口 和树枝模板在配置文件数据中添加关于页面的自定义详细信息。简而言之,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 - 通过在主节中添加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' 我们添加了两个用户,角色ROLE_USER中的 用户 和角色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步 - 现在,将安全性应用于应用程序的某个部分。 例如,将admin部分限制为角色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步 - 创建一个新的应用程序, 工作流程示例 。 cd /path/to/dev mkdir workflow-example cd workflow-example composer require symfony/workflow 第2步 -创建一个新的类, 离开 有 applied_by,leave_on 和 状态 属性。 class Leave { public $applied_by; public $leave_on; public $status; } 这里,applied_by指的是要离开的员工。leave_on指的是离开的日期。状态是指休假状态。 第3步 - 休假管理有四个地方,正在申请,正在审批和批准/拒绝。 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,批准 和 拒绝 。to_process转换接受离开申请并将申请的地点移至in_process。批准过渡批准休假申请并将该地点移至批准状态。同样,拒绝转换拒绝了请假申请,并将该地点拒绝。我们使用addTransition方法创建了所有转换。 第5步 - 使用构建方法构建定义。 $definition = $builder->build(); 第6步 - 可选择将定义转储为graphviz点格式,可将其转换为图像文件以供参考。 $dumper = new GraphvizDumper(); echo $dumper->dump($definition); 第7步 - 创建一个标记存储区,用于存储对象的当前位置/状态。 $marking = new SingleStateMarkingStore('status'); 在这里,我们使用了 SingleStateMarkingStore 类来创建标记,并将当前状态标记为对象的状态属性。在我们的例子中,该对象是离开对象。 第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 Symfony单元测试 Symfony REST版