📅  最后修改于: 2020-10-19 03:23:21             🧑  作者: Mango
在本章中,我们将学习Symfony框架中的一些高级概念。
在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');
完整的代码清单如下。
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 。可以在Web应用程序中启用Web事件探查器捆绑包,方法是在AppKernel的registerBundles方法中注册该捆绑包。
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
}
可以在应用程序配置文件app / config / config.xml的web_profile部分下配置Web配置文件组件
web_profiler:
toolbar: false
position: bottom
Symfony应用程序在页面底部将配置文件数据显示为不同的部分。
Symfony还提供了一种简单的方法,可以使用DataCollectorInterface接口和树枝模板在配置文件数据中添加有关页面的自定义详细信息。简而言之,Symfony通过提供相对容易的强大的概要分析框架,使Web开发人员能够创建世界一流的应用程序。
如前所述,Symfony通过其安全组件提供了一个强大的安全框架。安全组件分为以下四个子组件。
让我们使用一个简单的演示应用程序来学习认证和授权的概念。
步骤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(以及诸如profiler之类的开发工具)。
步骤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的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('This is admin section.');
}
步骤10-最后,访问管理页面以在浏览器中检查安全配置。浏览器将询问用户名和密码,仅允许配置的用户。
工作流是一个高级概念,已在许多企业应用程序中使用。在电子商务应用程序中,产品交付过程是一个工作流程。首先对产品开票(创建订单),从商店购买并包装(包装/准备发货),然后发货给用户。如果有任何问题,产品将从用户处退回,并恢复订单。行动流程的顺序非常重要。例如,我们不能在没有开票的情况下交付产品。
Symfony组件提供了一种定义和管理工作流的面向对象的方法。过程中的每个步骤都称为“位置” ,从一个位置移动到另一位置所需的动作称为“过渡” 。创建工作流的场所和过渡的集合称为工作流定义。
让我们通过创建一个简单的请假管理应用程序来了解工作流程的概念。
步骤1-创建一个新的应用程序,工作流程示例。
cd /path/to/dev
mkdir workflow-example
cd workflow-example
composer require symfony/workflow
步骤2-创建一个新类, Leave具有apply_by,leave_on和status属性。
class Leave {
public $applied_by;
public $leave_on;
public $status;
}
在这里,apply_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,approve和reject 。 to_process转换接受请假申请并将地点从Apply移至in_process。批准过渡批准休假申请并将地点移至已批准。同样,拒绝过渡将拒绝请假申请,并将该地点移至拒绝。我们使用addTransition方法创建了所有转换。
步骤5-使用构建方法构建定义。
$definition = $builder->build();
步骤6-可选地,可以将定义转储为graphviz点格式,可以将其转换为图像文件以供参考。
$dumper = new GraphvizDumper();
echo $dumper->dump($definition);
步骤7-创建标记存储,用于存储对象的当前位置/状态。
$marking = new SingleStateMarkingStore('status');
在这里,我们使用SingleStateMarkingStore类创建标记,并将当前状态标记到对象的status属性中。在我们的示例中,对象是Leave对象。
步骤8-使用定义和标记创建工作流程。
$leaveWorkflow = new Workflow($definition, $marking);
在这里,我们使用了Workflow类来创建工作流。
步骤9-使用注册表类将工作流添加到工作流框架的注册表中。
$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";
完整的编码如下-
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