配置文件
## 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等
服务容器
经常使用的类 及 被其他服务依赖的类 可以注册为服务
服务配置:
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, 监听器从大到小值调用