Symfony2 細節小計2

配置文件

## app/config/ 下
parameter.yml  # 因爲參數敏感,該文件被版本控制系統忽略
parameter.yml.dist  # 在parameter.yml中定義的參數須要也在這裏添加,該文件是它的配置模板
                    # 每次部署,該文件會與之比對,若有差別,symfony會要求提供新參數在parameter.yml中


請求

$request = Request::createFromGlobals();
$request->isXmlHttpRequest();
        ->getMethod();
        ->query->get('var', 'default');
        ->request->get();
        ->files->get();
        ->cookies->get();
        ->server->get();
        ->headers->get();
        ->getSession();
        ->isSecure(); //斷定https
        ->attributes->get('_controller, _route, {id} ....');
        ->getRequestFormat();
        ->getContent();

parameter bags通用方法
->get();
->has();
->all();


響應

$response = new Response($content, $Response::HTTP_OK);  //Response::HTTP_NOT_FOUND
$response->setStatusCode(Response::HTTP_OK);    
        ->headers->set('Content-Type', 'text/html');
        ->setContent('<html><body><h1>Hello world!</h1></body></html>');
        ->send();

JsonResponse,  BinaryFileResponse.

頁面報錯:
400 - throw Controller->createNotFoundException();
500 - throw new \Exception('Something wrong!');

FlashMessage:
Controller->addFlash('notice', 'Congratulations, your action succeeded!');

<div>
    {{ app.session.flashbag.get('notice') }}
</div>


Serializer

框架自帶組件:Serializer
第三方bundle:JMSSerializerBundle

啓用序列化組件(服務啓用後默認配備JsonEncoder和XmlEncoder,但沒有標準化器 normalizers)
# app/config/config.yml
framework:
    serializer:
        enabled: true
        
給服務打tag以標註其爲normalizer或encoder, 從而在序列化載入時一塊兒載入它們
# app/config/config.yml
services:
   get_set_method_normalizer:
      class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
      tags:
         - { name: serializer.normalizer }
         
#使用序列化時僅需配置序列化器的可用服務:
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$serializer = new Serializer($normalizers, $encoders);

$person = new Acme\Person();
$jsonContent = $serializer->serialize($person, 'json');  //第二參指定encoder


#忽略部分部分屬性的序列化
$normalizer = new GetSetMethodNormalizer();
$normalizer->setIgnoredAttributes(array('age'));

#反序列化
$data = "<person><name>foo</name><age>99</age><sportsman>false</sportsman></person>";
$person = $serializer->deserialize($data, 'Acme\Person', 'xml'); #第二參指定需生成的類, 第三參指定encoder


常見Bundle用途

sensio/framework-extra-bundle  #提供在控制器中註解命令的支持,自帶
jms/di-extra-bundle  #提供註解命令實現屬性注入的支持
doctrine/doctrine-fixtures-bundle  #提供data fixture支持


Bundle建立流程

php app/console generate:bundle ------ bundle目錄
                                    |-AppKernel->registerBundles(); //內核註冊Bundle
                                    |-app/config/routing.yml; //路由配置
                                    |-app/config/config.yml; //Bundle配置

bundle路徑格式:
@BUNDLE_NAME/path/to/file //註解命令索引bundle文件
BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME //索引action操做


安裝第三方Bundle

1.composer安裝bundle
2.內核註冊啓用bundle
3.配置bundle(app/config/config.yml及bundle內配置文件)
    app/console config:dump-reference AsseticBundle //dump出bundle配置幫助
    bundle/Resource/doc/index.md   //bundle配置幫助文件


調試

dump($articles); //控制器調試
{{ dump(articles) }} //模板調試(必須TwigBundle的debug配置爲true)

debug toolbar 只有在render()調用事後纔會顯示


性能

1.安裝apc字節碼緩存拓展, 並關閉文件檢查php.ini => apc.stat=0 轉爲手動管理 代碼更新 引發的 apc緩存失效
2.web/app.php 打開 ApcClassLoader
3.使用引導文件 web/app.php 啓用 require_once __DIR__.'/../app/bootstrap.php.cache' //類定義聚合文件
4.使用http反向代理緩存系統AppCache或者專業的Varnish, Squid等


服務容器

常常使用的類 及 被其餘服務依賴的類 能夠註冊爲服務
php

服務配置:
services:
    service_name:
        public: bool,  #false時, 該服務僅能用於配置定義依賴, 不能再容器中取出
        class: Acme\HelloBundle\Mailer
        arguments: []
        file: %kernel.root_dir%/src/path/to/file/foo.php #加載服務前提依賴文件
        tags:
            - {name: xxx}
# 通常的, service_name會定義爲目錄名加類名,從而避免同名衝突, 如這裏的:acme.hello.mailer
# 固然, 其餘合理的短名稱也是能夠的
                        
            
容器調用:
$controller->get($service_name);

YAML格式的數組參數定義:
方式1: 數組括號【】
方式2: 前導橫線 「-    {item1}「

YAML文件導入外部資源:
方式1: imports命令
    imports:
    - { resource: "@AcmeHelloBundle/Resources/config/services.yml" }
方式2:
    使用服務拓展類導入配置
    
ServiceContainer配置:
services:
    my_mailer:        # ...
    newsletter_manager:
        class:     Acme\HelloBundle\Newsletter\NewsletterManager
        arguments: [無引號string參數,@my_mailer, @?my_mailer, "@=service('mailer_configuration').getMailerMethod()"]    #申明一個依賴(構造函數注入), 申明一個可選依賴(構造函數注入, 參數須帶默認值), 申明依賴一個表達式值
        calls:
            - [setMailer, ["@my_mailer"]] #申明可選依賴(setter注入)
            
服務別名($container->get('bar'); ):
services:
   foo:     
       class: Acme\HelloBundle\Foo
   bar:
       alias: foo


DI組件

標準化並集中管理對象的初始化

############ 原始類 #############
class Mailer{
    private $transport;

    public function __construct($transport)
    {
        $this->transport = $transport;
    }
}
class NewsletterManager{
    private $mailer;

    public function __construct(\Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
    
    public function setMailer(\Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
}

########## AAAAAA ###########
## PHP代碼來註冊服務
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
    ->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%'); //或者直接傳參 ->addArgument('sendmail');
$container 
    ->register('newsletter_manager', 'NewsletterManager')
    ->addArgument(new Reference('mailer')); //構造函數注入
$container
    ->register('newsletter_manager', 'NewsletterManager')
    ->addMethodCall('setMailer', array(new Reference('mailer'))); //setter注入
######## BBBBBB ##########
## 加載配置文件來註冊服務
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.yml');
######## CCCCCC ##########
## 直接在配置文件中註冊服務(即服務容器配置)




################################
####### 得到容器中的服務 ########
use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$newsletterManager = $container->get('newsletter_manager');


Symfony內部機制

## 前端控制器
$kernel = new AppKernel('環境', '是否debug模式');


框架層次:
FrameworkBundle
^
HttpKernel  處理http動態部分
^
HttpFoundation  最底層, 抽象出了一組http處理的對象

內核依賴ControllerResolverInterface的一個實現來選擇合適的控制器, 接口聲明瞭兩個方法:
    public function getController(Request $request); //默認將經過RouterListener去定義了Request::_controller屬性(相似於Bundle\BlogBundle\PostController:indexAction), 最後返回callable形式的controller
    public function getArguments(Request $request, $controller); //默認在Requestion屬性中尋找(同名匹配action須要的參數)
    
AppKernel::handle(Request, RequestType, EnableException)的內部實現依賴Resolver和一組有序時間通知鏈 從而 將 請求 轉爲 響應


應用生命週期, HttpKernel拋出KernelEvents事件,監聽器收到相應事件類:  

    1.kernel.request,事件類GetResponseEvent. 在request dispatching的最初時拋出
    此處可在框架流程開始前就返回響應(任意相關監聽器經過setResponse方法在Event上寫入Response。若是有監聽器返回了Response, 則後續監聽器直接略過, 直接跳8)
    通常的FrameworkBundle此處經過RouteListener計算Request::_controller。
    通常掛載在此處的監聽器須要判斷下$event->isMasterRequest(), 避免響應內部子請求。
    |
    V
    2.調用Resolver來找到Controller(callable形式)
    |
    V
    3.kernel.controller,事件類FilterControllerEvent. 當請求匹配到controller時拋出
    可在此處切換控制器
    |
    V
    4.kernel檢查Controller是有效callable
    |
    V
    5.調用Resolver來找到參數傳給Controller
    |
    V
    6.Kernel正式調用Controller
    |
    V
    7.kernel.view,事件類GetResponseForControllerResultEvent,當Controller返回非Response實例時拋出
    此處可爲controller返回值建立response(監聽器獲取原始值$event->getControllerResult()並將其轉化爲Response)
    |
    V
    8.kernel.response,事件類FilterResponseEvent, 當針對請求建立了response時拋出
    此處可調整或替換response(獲取響應$event->getResponse())
    |
    V    
    9.返回Response
    |
    V
    10.kernel.finish_request, 事件類FinishRequestEvent,當針對請求生成好http響應時拋出
    若是請求致使調整了應用環境狀態,可在此處理, 不管成功或異常都會執行到這
    |
    V
    11.kernel.terminate,事件類PostResponseEvent,當response送出後拋出
    可作些收尾工做
    
    kernel.exception,事件類GetResponseForExceptionEvent,當應用內部未能捕獲掉異常時拋出
    此處可處理異常拋出後如何響應或進一步調整異常。異常處理成功, 則進入8。失敗, 則將異常從新拋出。
    FramworkBundle註冊了ExceptionListener來將請求引導到特定控制器
    在內部請求處理時, 不但願捕獲異常, 可在handle()方法第三參傳false來關閉kernel.exception事件.
    
    
    監聽器處理 kernel.request, kernel.view or kernel.exception 時, 設定了response後, 事件再也不傳遞, 於是這些事件的低優先級監聽器不會被調用到
    
#############################################################################################################################################################

FramworkBundle註冊了以下監聽器:
ProfilerListener 從當前Request中收集數據
WebDebugToolbarListener 注入web調試工具條
ResponserListener 填寫響應的content-type信息
EsiListener 添加一個代理控制http頭,若是響應存在Esi標籤

事件調試:
app/console debug:event-dispatcher 【事件名】
    
內部請求:
每次主請求期間能夠執行子請求, 在handle()第二參傳入請求類型RequestType
HttpKernelInterface::MASTER_REQUEST;
HttpKernelInterface::SUB_REQUEST;


事件:
Kernel可用Event都繼承自KernelEvent.
KernelEvent ->getRequestType();
            ->isMasterRequest();
            ->getKernel();
            ->getRequest();
            
獲取調試信息:
$profile = $container->get('profiler')->loadProfileFromResponse($response);
$profile = $container->get('profiler')->loadProfile($token);

導出導入調試信息:
$data = $profiler->export($profile);
$profiler->import($data);

獲取調試token(也保存在http響應頭X-Debug-Token):
$tokens = $container->get($host_ip, $url_pattern, $latest_count, $time_begin, $time_end);


事件監聽

symfony自帶許多事件、鉤子
由 HttpKernel 組件拋出(定義在KernelEvents類中)

監聽事件流程:
1. 建立事件監聽器
class AcmeExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();
        if ($exception instanceof HttpExceptionInterface) {}
        //....
        $event->setResponse($response); //對象上設定response(對象是傳引用)
    }
}
2. 監聽器註冊爲服務, 並綁定到事件
# app/config/services.yml
services:
    kernel.listener.your_listener_name:
        class: AppBundle\EventListener\AcmeExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: xxx }  #priority參數可選, 默認爲0, 範圍-255 ~ 255, 監聽器從大到小值調用
相關文章
相關標籤/搜索