
- 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