PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 2ms

/app/cache/dev/classes.php

https://bitbucket.org/polipetr/cms
PHP | 10686 lines | 7027 code | 2355 blank | 1304 comment | 903 complexity | cef9856e75cbacf1883e769170a0751e MD5 | raw file
Possible License(s): Apache-2.0, LGPL-3.0, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. namespace Symfony\Component\EventDispatcher
  3. {
  4. interface EventSubscriberInterface
  5. {
  6. public static function getSubscribedEvents();
  7. }
  8. }
  9. namespace Symfony\Bundle\FrameworkBundle\EventListener
  10. {
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. use Symfony\Component\HttpKernel\HttpKernelInterface;
  13. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  14. use Symfony\Component\HttpKernel\KernelEvents;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. class SessionListener implements EventSubscriberInterface
  17. {
  18. private $container;
  19. public function __construct(ContainerInterface $container)
  20. {
  21. $this->container = $container;
  22. }
  23. public function onKernelRequest(GetResponseEvent $event)
  24. {
  25. if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  26. return;
  27. }
  28. $request = $event->getRequest();
  29. if (!$this->container->has('session') || $request->hasSession()) {
  30. return;
  31. }
  32. $request->setSession($this->container->get('session'));
  33. }
  34. public static function getSubscribedEvents()
  35. {
  36. return array(
  37. KernelEvents::REQUEST => array('onKernelRequest', 128),
  38. );
  39. }
  40. }
  41. }
  42. namespace Symfony\Component\HttpFoundation\Session\Storage
  43. {
  44. use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
  45. use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
  46. interface SessionStorageInterface
  47. {
  48. public function start();
  49. public function isStarted();
  50. public function getId();
  51. public function setId($id);
  52. public function getName();
  53. public function setName($name);
  54. public function regenerate($destroy = false, $lifetime = null);
  55. public function save();
  56. public function clear();
  57. public function getBag($name);
  58. public function registerBag(SessionBagInterface $bag);
  59. public function getMetadataBag();
  60. }
  61. }
  62. namespace Symfony\Component\HttpFoundation\Session\Storage
  63. {
  64. use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
  65. use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
  66. use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
  67. use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
  68. use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
  69. class NativeSessionStorage implements SessionStorageInterface
  70. {
  71. protected $bags;
  72. protected $started = false;
  73. protected $closed = false;
  74. protected $saveHandler;
  75. protected $metadataBag;
  76. public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
  77. {
  78. ini_set('session.cache_limiter', ''); ini_set('session.use_cookies', 1);
  79. if (version_compare(phpversion(), '5.4.0', '>=')) {
  80. session_register_shutdown();
  81. } else {
  82. register_shutdown_function('session_write_close');
  83. }
  84. $this->setMetadataBag($metaBag);
  85. $this->setOptions($options);
  86. $this->setSaveHandler($handler);
  87. }
  88. public function getSaveHandler()
  89. {
  90. return $this->saveHandler;
  91. }
  92. public function start()
  93. {
  94. if ($this->started && !$this->closed) {
  95. return true;
  96. }
  97. if (!$this->started && !$this->closed && $this->saveHandler->isActive()
  98. && $this->saveHandler->isSessionHandlerInterface()) {
  99. $this->loadSession();
  100. return true;
  101. }
  102. if (ini_get('session.use_cookies') && headers_sent()) {
  103. throw new \RuntimeException('Failed to start the session because headers have already been sent.');
  104. }
  105. if (!session_start()) {
  106. throw new \RuntimeException('Failed to start the session');
  107. }
  108. $this->loadSession();
  109. if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
  110. $this->saveHandler->setActive(false);
  111. }
  112. return true;
  113. }
  114. public function getId()
  115. {
  116. if (!$this->started) {
  117. return ''; }
  118. return $this->saveHandler->getId();
  119. }
  120. public function setId($id)
  121. {
  122. $this->saveHandler->setId($id);
  123. }
  124. public function getName()
  125. {
  126. return $this->saveHandler->getName();
  127. }
  128. public function setName($name)
  129. {
  130. $this->saveHandler->setName($name);
  131. }
  132. public function regenerate($destroy = false, $lifetime = null)
  133. {
  134. if (null !== $lifetime) {
  135. ini_set('session.cookie_lifetime', $lifetime);
  136. }
  137. if ($destroy) {
  138. $this->metadataBag->stampNew();
  139. }
  140. return session_regenerate_id($destroy);
  141. }
  142. public function save()
  143. {
  144. session_write_close();
  145. if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
  146. $this->saveHandler->setActive(false);
  147. }
  148. $this->closed = true;
  149. }
  150. public function clear()
  151. {
  152. foreach ($this->bags as $bag) {
  153. $bag->clear();
  154. }
  155. $_SESSION = array();
  156. $this->loadSession();
  157. }
  158. public function registerBag(SessionBagInterface $bag)
  159. {
  160. $this->bags[$bag->getName()] = $bag;
  161. }
  162. public function getBag($name)
  163. {
  164. if (!isset($this->bags[$name])) {
  165. throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
  166. }
  167. if ($this->saveHandler->isActive() && !$this->started) {
  168. $this->loadSession();
  169. } elseif (!$this->started) {
  170. $this->start();
  171. }
  172. return $this->bags[$name];
  173. }
  174. public function setMetadataBag(MetadataBag $metaBag = null)
  175. {
  176. if (null === $metaBag) {
  177. $metaBag = new MetadataBag();
  178. }
  179. $this->metadataBag = $metaBag;
  180. }
  181. public function getMetadataBag()
  182. {
  183. return $this->metadataBag;
  184. }
  185. public function isStarted()
  186. {
  187. return $this->started;
  188. }
  189. public function setOptions(array $options)
  190. {
  191. $validOptions = array_flip(array(
  192. 'cache_limiter', 'cookie_domain', 'cookie_httponly',
  193. 'cookie_lifetime', 'cookie_path', 'cookie_secure',
  194. 'entropy_file', 'entropy_length', 'gc_divisor',
  195. 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
  196. 'hash_function', 'name', 'referer_check',
  197. 'serialize_handler', 'use_cookies',
  198. 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
  199. 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
  200. 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
  201. ));
  202. foreach ($options as $key => $value) {
  203. if (isset($validOptions[$key])) {
  204. ini_set('session.'.$key, $value);
  205. }
  206. }
  207. }
  208. public function setSaveHandler($saveHandler = null)
  209. {
  210. if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
  211. $saveHandler = new SessionHandlerProxy($saveHandler);
  212. } elseif (!$saveHandler instanceof AbstractProxy) {
  213. $saveHandler = new NativeProxy();
  214. }
  215. $this->saveHandler = $saveHandler;
  216. if ($this->saveHandler instanceof \SessionHandlerInterface) {
  217. if (version_compare(phpversion(), '5.4.0', '>=')) {
  218. session_set_save_handler($this->saveHandler, false);
  219. } else {
  220. session_set_save_handler(
  221. array($this->saveHandler, 'open'),
  222. array($this->saveHandler, 'close'),
  223. array($this->saveHandler, 'read'),
  224. array($this->saveHandler, 'write'),
  225. array($this->saveHandler, 'destroy'),
  226. array($this->saveHandler, 'gc')
  227. );
  228. }
  229. }
  230. }
  231. protected function loadSession(array &$session = null)
  232. {
  233. if (null === $session) {
  234. $session = &$_SESSION;
  235. }
  236. $bags = array_merge($this->bags, array($this->metadataBag));
  237. foreach ($bags as $bag) {
  238. $key = $bag->getStorageKey();
  239. $session[$key] = isset($session[$key]) ? $session[$key] : array();
  240. $bag->initialize($session[$key]);
  241. }
  242. $this->started = true;
  243. $this->closed = false;
  244. }
  245. }
  246. }
  247. namespace Symfony\Component\HttpFoundation\Session\Storage\Handler
  248. {
  249. if (version_compare(phpversion(), '5.4.0', '>=')) {
  250. class NativeSessionHandler extends \SessionHandler {}
  251. } else {
  252. class NativeSessionHandler {}
  253. }
  254. }
  255. namespace Symfony\Component\HttpFoundation\Session\Storage\Handler
  256. {
  257. class NativeFileSessionHandler extends NativeSessionHandler
  258. {
  259. public function __construct($savePath = null)
  260. {
  261. if (null === $savePath) {
  262. $savePath = ini_get('session.save_path');
  263. }
  264. $baseDir = $savePath;
  265. if ($count = substr_count($savePath, ';')) {
  266. if ($count > 2) {
  267. throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
  268. }
  269. $baseDir = ltrim(strrchr($savePath, ';'), ';');
  270. }
  271. if ($baseDir && !is_dir($baseDir)) {
  272. mkdir($baseDir, 0777, true);
  273. }
  274. ini_set('session.save_path', $savePath);
  275. ini_set('session.save_handler', 'files');
  276. }
  277. }
  278. }
  279. namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy
  280. {
  281. abstract class AbstractProxy
  282. {
  283. protected $wrapper = false;
  284. protected $active = false;
  285. protected $saveHandlerName;
  286. public function getSaveHandlerName()
  287. {
  288. return $this->saveHandlerName;
  289. }
  290. public function isSessionHandlerInterface()
  291. {
  292. return ($this instanceof \SessionHandlerInterface);
  293. }
  294. public function isWrapper()
  295. {
  296. return $this->wrapper;
  297. }
  298. public function isActive()
  299. {
  300. return $this->active;
  301. }
  302. public function setActive($flag)
  303. {
  304. $this->active = (bool) $flag;
  305. }
  306. public function getId()
  307. {
  308. return session_id();
  309. }
  310. public function setId($id)
  311. {
  312. if ($this->isActive()) {
  313. throw new \LogicException('Cannot change the ID of an active session');
  314. }
  315. session_id($id);
  316. }
  317. public function getName()
  318. {
  319. return session_name();
  320. }
  321. public function setName($name)
  322. {
  323. if ($this->isActive()) {
  324. throw new \LogicException('Cannot change the name of an active session');
  325. }
  326. session_name($name);
  327. }
  328. }
  329. }
  330. namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy
  331. {
  332. class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
  333. {
  334. protected $handler;
  335. public function __construct(\SessionHandlerInterface $handler)
  336. {
  337. $this->handler = $handler;
  338. $this->wrapper = ($handler instanceof \SessionHandler);
  339. $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
  340. }
  341. public function open($savePath, $sessionName)
  342. {
  343. $return = (bool) $this->handler->open($savePath, $sessionName);
  344. if (true === $return) {
  345. $this->active = true;
  346. }
  347. return $return;
  348. }
  349. public function close()
  350. {
  351. $this->active = false;
  352. return (bool) $this->handler->close();
  353. }
  354. public function read($id)
  355. {
  356. return (string) $this->handler->read($id);
  357. }
  358. public function write($id, $data)
  359. {
  360. return (bool) $this->handler->write($id, $data);
  361. }
  362. public function destroy($id)
  363. {
  364. return (bool) $this->handler->destroy($id);
  365. }
  366. public function gc($maxlifetime)
  367. {
  368. return (bool) $this->handler->gc($maxlifetime);
  369. }
  370. }
  371. }
  372. namespace Symfony\Component\HttpFoundation\Session
  373. {
  374. use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
  375. interface SessionInterface
  376. {
  377. public function start();
  378. public function getId();
  379. public function setId($id);
  380. public function getName();
  381. public function setName($name);
  382. public function invalidate($lifetime = null);
  383. public function migrate($destroy = false, $lifetime = null);
  384. public function save();
  385. public function has($name);
  386. public function get($name, $default = null);
  387. public function set($name, $value);
  388. public function all();
  389. public function replace(array $attributes);
  390. public function remove($name);
  391. public function clear();
  392. public function isStarted();
  393. public function registerBag(SessionBagInterface $bag);
  394. public function getBag($name);
  395. public function getMetadataBag();
  396. }
  397. }
  398. namespace Symfony\Component\HttpFoundation\Session
  399. {
  400. use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
  401. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
  402. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  403. use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
  404. use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
  405. use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
  406. use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
  407. class Session implements SessionInterface, \IteratorAggregate, \Countable
  408. {
  409. protected $storage;
  410. private $flashName;
  411. private $attributeName;
  412. public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
  413. {
  414. $this->storage = $storage ?: new NativeSessionStorage();
  415. $attributes = $attributes ?: new AttributeBag();
  416. $this->attributeName = $attributes->getName();
  417. $this->registerBag($attributes);
  418. $flashes = $flashes ?: new FlashBag();
  419. $this->flashName = $flashes->getName();
  420. $this->registerBag($flashes);
  421. }
  422. public function start()
  423. {
  424. return $this->storage->start();
  425. }
  426. public function has($name)
  427. {
  428. return $this->storage->getBag($this->attributeName)->has($name);
  429. }
  430. public function get($name, $default = null)
  431. {
  432. return $this->storage->getBag($this->attributeName)->get($name, $default);
  433. }
  434. public function set($name, $value)
  435. {
  436. $this->storage->getBag($this->attributeName)->set($name, $value);
  437. }
  438. public function all()
  439. {
  440. return $this->storage->getBag($this->attributeName)->all();
  441. }
  442. public function replace(array $attributes)
  443. {
  444. $this->storage->getBag($this->attributeName)->replace($attributes);
  445. }
  446. public function remove($name)
  447. {
  448. return $this->storage->getBag($this->attributeName)->remove($name);
  449. }
  450. public function clear()
  451. {
  452. $this->storage->getBag($this->attributeName)->clear();
  453. }
  454. public function isStarted()
  455. {
  456. return $this->storage->isStarted();
  457. }
  458. public function getIterator()
  459. {
  460. return new \ArrayIterator($this->storage->getBag($this->attributeName)->all());
  461. }
  462. public function count()
  463. {
  464. return count($this->storage->getBag($this->attributeName)->all());
  465. }
  466. public function invalidate($lifetime = null)
  467. {
  468. $this->storage->clear();
  469. return $this->migrate(true, $lifetime);
  470. }
  471. public function migrate($destroy = false, $lifetime = null)
  472. {
  473. return $this->storage->regenerate($destroy, $lifetime);
  474. }
  475. public function save()
  476. {
  477. $this->storage->save();
  478. }
  479. public function getId()
  480. {
  481. return $this->storage->getId();
  482. }
  483. public function setId($id)
  484. {
  485. $this->storage->setId($id);
  486. }
  487. public function getName()
  488. {
  489. return $this->storage->getName();
  490. }
  491. public function setName($name)
  492. {
  493. $this->storage->setName($name);
  494. }
  495. public function getMetadataBag()
  496. {
  497. return $this->storage->getMetadataBag();
  498. }
  499. public function registerBag(SessionBagInterface $bag)
  500. {
  501. $this->storage->registerBag($bag);
  502. }
  503. public function getBag($name)
  504. {
  505. return $this->storage->getBag($name);
  506. }
  507. public function getFlashBag()
  508. {
  509. return $this->getBag($this->flashName);
  510. }
  511. public function getFlashes()
  512. {
  513. $all = $this->getBag($this->flashName)->all();
  514. $return = array();
  515. if ($all) {
  516. foreach ($all as $name => $array) {
  517. if (is_numeric(key($array))) {
  518. $return[$name] = reset($array);
  519. } else {
  520. $return[$name] = $array;
  521. }
  522. }
  523. }
  524. return $return;
  525. }
  526. public function setFlashes($values)
  527. {
  528. foreach ($values as $name => $value) {
  529. $this->getBag($this->flashName)->set($name, $value);
  530. }
  531. }
  532. public function getFlash($name, $default = null)
  533. {
  534. $return = $this->getBag($this->flashName)->get($name);
  535. return empty($return) ? $default : reset($return);
  536. }
  537. public function setFlash($name, $value)
  538. {
  539. $this->getBag($this->flashName)->set($name, $value);
  540. }
  541. public function hasFlash($name)
  542. {
  543. return $this->getBag($this->flashName)->has($name);
  544. }
  545. public function removeFlash($name)
  546. {
  547. $this->getBag($this->flashName)->get($name);
  548. }
  549. public function clearFlashes()
  550. {
  551. return $this->getBag($this->flashName)->clear();
  552. }
  553. }
  554. }
  555. namespace Symfony\Component\Routing
  556. {
  557. interface RequestContextAwareInterface
  558. {
  559. public function setContext(RequestContext $context);
  560. public function getContext();
  561. }
  562. }
  563. namespace Symfony\Component\Routing\Generator
  564. {
  565. use Symfony\Component\Routing\RequestContextAwareInterface;
  566. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  567. interface UrlGeneratorInterface extends RequestContextAwareInterface
  568. {
  569. public function generate($name, $parameters = array(), $absolute = false);
  570. }
  571. }
  572. namespace Symfony\Component\Routing\Generator
  573. {
  574. interface ConfigurableRequirementsInterface
  575. {
  576. public function setStrictRequirements($enabled);
  577. public function isStrictRequirements();
  578. }
  579. }
  580. namespace Symfony\Component\Routing\Generator
  581. {
  582. use Symfony\Component\Routing\RouteCollection;
  583. use Symfony\Component\Routing\RequestContext;
  584. use Symfony\Component\Routing\Exception\InvalidParameterException;
  585. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  586. use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
  587. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  588. class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
  589. {
  590. protected $context;
  591. protected $strictRequirements = true;
  592. protected $logger;
  593. protected $decodedChars = array(
  594. '%2F' => '/',
  595. '%40' => '@',
  596. '%3A' => ':',
  597. '%3B' => ';',
  598. '%2C' => ',',
  599. '%3D' => '=',
  600. '%2B' => '+',
  601. '%21' => '!',
  602. '%2A' => '*',
  603. '%7C' => '|',
  604. );
  605. protected $routes;
  606. public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
  607. {
  608. $this->routes = $routes;
  609. $this->context = $context;
  610. $this->logger = $logger;
  611. }
  612. public function setContext(RequestContext $context)
  613. {
  614. $this->context = $context;
  615. }
  616. public function getContext()
  617. {
  618. return $this->context;
  619. }
  620. public function setStrictRequirements($enabled)
  621. {
  622. $this->strictRequirements = (Boolean) $enabled;
  623. }
  624. public function isStrictRequirements()
  625. {
  626. return $this->strictRequirements;
  627. }
  628. public function generate($name, $parameters = array(), $absolute = false)
  629. {
  630. if (null === $route = $this->routes->get($name)) {
  631. throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
  632. }
  633. $compiledRoute = $route->compile();
  634. return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute);
  635. }
  636. protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
  637. {
  638. $variables = array_flip($variables);
  639. $originParameters = $parameters;
  640. $parameters = array_replace($this->context->getParameters(), $parameters);
  641. $tparams = array_replace($defaults, $parameters);
  642. if ($diff = array_diff_key($variables, $tparams)) {
  643. throw new MissingMandatoryParametersException(sprintf('The "%s" route has some missing mandatory parameters ("%s").', $name, implode('", "', array_keys($diff))));
  644. }
  645. $url = '';
  646. $optional = true;
  647. foreach ($tokens as $token) {
  648. if ('variable' === $token[0]) {
  649. if (false === $optional || !array_key_exists($token[3], $defaults) || (isset($parameters[$token[3]]) && (string) $parameters[$token[3]] != (string) $defaults[$token[3]])) {
  650. if (!$isEmpty = in_array($tparams[$token[3]], array(null, '', false), true)) {
  651. if ($tparams[$token[3]] && !preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) {
  652. $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]]);
  653. if ($this->strictRequirements) {
  654. throw new InvalidParameterException($message);
  655. }
  656. if ($this->logger) {
  657. $this->logger->err($message);
  658. }
  659. return null;
  660. }
  661. }
  662. if (!$isEmpty || !$optional) {
  663. $url = $token[1].$tparams[$token[3]].$url;
  664. }
  665. $optional = false;
  666. }
  667. } elseif ('text' === $token[0]) {
  668. $url = $token[1].$url;
  669. $optional = false;
  670. }
  671. }
  672. if ('' === $url) {
  673. $url = '/';
  674. }
  675. $url = $this->context->getBaseUrl().strtr(rawurlencode($url), $this->decodedChars);
  676. $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
  677. if ('/..' === substr($url, -3)) {
  678. $url = substr($url, 0, -2) . '%2E%2E';
  679. } elseif ('/.' === substr($url, -2)) {
  680. $url = substr($url, 0, -1) . '%2E';
  681. }
  682. $extra = array_diff_key($originParameters, $variables, $defaults);
  683. if ($extra && $query = http_build_query($extra, '', '&')) {
  684. $url .= '?'.$query;
  685. }
  686. if ($this->context->getHost()) {
  687. $scheme = $this->context->getScheme();
  688. if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
  689. $absolute = true;
  690. $scheme = $req;
  691. }
  692. if ($absolute) {
  693. $port = '';
  694. if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
  695. $port = ':'.$this->context->getHttpPort();
  696. } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
  697. $port = ':'.$this->context->getHttpsPort();
  698. }
  699. $url = $scheme.'://'.$this->context->getHost().$port.$url;
  700. }
  701. }
  702. return $url;
  703. }
  704. }
  705. }
  706. namespace Symfony\Component\Routing
  707. {
  708. use Symfony\Component\HttpFoundation\Request;
  709. class RequestContext
  710. {
  711. private $baseUrl;
  712. private $method;
  713. private $host;
  714. private $scheme;
  715. private $httpPort;
  716. private $httpsPort;
  717. private $parameters;
  718. public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443)
  719. {
  720. $this->baseUrl = $baseUrl;
  721. $this->method = strtoupper($method);
  722. $this->host = $host;
  723. $this->scheme = strtolower($scheme);
  724. $this->httpPort = $httpPort;
  725. $this->httpsPort = $httpsPort;
  726. $this->parameters = array();
  727. }
  728. public function fromRequest(Request $request)
  729. {
  730. $this->setBaseUrl($request->getBaseUrl());
  731. $this->setMethod($request->getMethod());
  732. $this->setHost($request->getHost());
  733. $this->setScheme($request->getScheme());
  734. $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort());
  735. $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort);
  736. }
  737. public function getBaseUrl()
  738. {
  739. return $this->baseUrl;
  740. }
  741. public function setBaseUrl($baseUrl)
  742. {
  743. $this->baseUrl = $baseUrl;
  744. }
  745. public function getMethod()
  746. {
  747. return $this->method;
  748. }
  749. public function setMethod($method)
  750. {
  751. $this->method = strtoupper($method);
  752. }
  753. public function getHost()
  754. {
  755. return $this->host;
  756. }
  757. public function setHost($host)
  758. {
  759. $this->host = $host;
  760. }
  761. public function getScheme()
  762. {
  763. return $this->scheme;
  764. }
  765. public function setScheme($scheme)
  766. {
  767. $this->scheme = strtolower($scheme);
  768. }
  769. public function getHttpPort()
  770. {
  771. return $this->httpPort;
  772. }
  773. public function setHttpPort($httpPort)
  774. {
  775. $this->httpPort = $httpPort;
  776. }
  777. public function getHttpsPort()
  778. {
  779. return $this->httpsPort;
  780. }
  781. public function setHttpsPort($httpsPort)
  782. {
  783. $this->httpsPort = $httpsPort;
  784. }
  785. public function getParameters()
  786. {
  787. return $this->parameters;
  788. }
  789. public function setParameters(array $parameters)
  790. {
  791. $this->parameters = $parameters;
  792. return $this;
  793. }
  794. public function getParameter($name)
  795. {
  796. return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
  797. }
  798. public function hasParameter($name)
  799. {
  800. return array_key_exists($name, $this->parameters);
  801. }
  802. public function setParameter($name, $parameter)
  803. {
  804. $this->parameters[$name] = $parameter;
  805. }
  806. }
  807. }
  808. namespace Symfony\Component\Routing\Matcher
  809. {
  810. use Symfony\Component\Routing\RequestContextAwareInterface;
  811. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  812. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  813. interface UrlMatcherInterface extends RequestContextAwareInterface
  814. {
  815. public function match($pathinfo);
  816. }
  817. }
  818. namespace Symfony\Component\Routing
  819. {
  820. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  821. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  822. interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface
  823. {
  824. public function getRouteCollection();
  825. }
  826. }
  827. namespace Symfony\Component\Routing
  828. {
  829. use Symfony\Component\Config\Loader\LoaderInterface;
  830. use Symfony\Component\Config\ConfigCache;
  831. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  832. use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
  833. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  834. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  835. class Router implements RouterInterface
  836. {
  837. protected $matcher;
  838. protected $generator;
  839. protected $context;
  840. protected $loader;
  841. protected $collection;
  842. protected $resource;
  843. protected $options;
  844. protected $logger;
  845. public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
  846. {
  847. $this->loader = $loader;
  848. $this->resource = $resource;
  849. $this->logger = $logger;
  850. $this->context = null === $context ? new RequestContext() : $context;
  851. $this->setOptions($options);
  852. }
  853. public function setOptions(array $options)
  854. {
  855. $this->options = array(
  856. 'cache_dir' => null,
  857. 'debug' => false,
  858. 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  859. 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  860. 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
  861. 'generator_cache_class' => 'ProjectUrlGenerator',
  862. 'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  863. 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  864. 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
  865. 'matcher_cache_class' => 'ProjectUrlMatcher',
  866. 'resource_type' => null,
  867. 'strict_requirements' => true,
  868. );
  869. $invalid = array();
  870. foreach ($options as $key => $value) {
  871. if (array_key_exists($key, $this->options)) {
  872. $this->options[$key] = $value;
  873. } else {
  874. $invalid[] = $key;
  875. }
  876. }
  877. if ($invalid) {
  878. throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('\', \'', $invalid)));
  879. }
  880. }
  881. public function setOption($key, $value)
  882. {
  883. if (!array_key_exists($key, $this->options)) {
  884. throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
  885. }
  886. $this->options[$key] = $value;
  887. }
  888. public function getOption($key)
  889. {
  890. if (!array_key_exists($key, $this->options)) {
  891. throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
  892. }
  893. return $this->options[$key];
  894. }
  895. public function getRouteCollection()
  896. {
  897. if (null === $this->collection) {
  898. $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
  899. }
  900. return $this->collection;
  901. }
  902. public function setContext(RequestContext $context)
  903. {
  904. $this->context = $context;
  905. if (null !== $this->matcher) {
  906. $this->getMatcher()->setContext($context);
  907. }
  908. if (null !== $this->generator) {
  909. $this->getGenerator()->setContext($context);
  910. }
  911. }
  912. public function getContext()
  913. {
  914. return $this->context;
  915. }
  916. public function generate($name, $parameters = array(), $absolute = false)
  917. {
  918. return $this->getGenerator()->generate($name, $parameters, $absolute);
  919. }
  920. public function match($pathinfo)
  921. {
  922. return $this->getMatcher()->match($pathinfo);
  923. }
  924. public function getMatcher()
  925. {
  926. if (null !== $this->matcher) {
  927. return $this->matcher;
  928. }
  929. if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
  930. return $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
  931. }
  932. $class = $this->options['matcher_cache_class'];
  933. $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
  934. if (!$cache->isFresh($class)) {
  935. $dumper = new $this->options['matcher_dumper_class']($this->getRouteCollection());
  936. $options = array(
  937. 'class' => $class,
  938. 'base_class' => $this->options['matcher_base_class'],
  939. );
  940. $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
  941. }
  942. require_once $cache;
  943. return $this->matcher = new $class($this->context);
  944. }
  945. public function getGenerator()
  946. {
  947. if (null !== $this->generator) {
  948. return $this->generator;
  949. }
  950. if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
  951. $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
  952. } else {
  953. $class = $this->options['generator_cache_class'];
  954. $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
  955. if (!$cache->isFresh($class)) {
  956. $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection());
  957. $options = array(
  958. 'class' => $class,
  959. 'base_class' => $this->options['generator_base_class'],
  960. );
  961. $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
  962. }
  963. require_once $cache;
  964. $this->generator = new $class($this->context, $this->logger);
  965. }
  966. if ($this->generator instanceof ConfigurableRequirementsInterface) {
  967. $this->generator->setStrictRequirements($this->options['strict_requirements']);
  968. }
  969. return $this->generator;
  970. }
  971. }
  972. }
  973. namespace Symfony\Component\Routing\Matcher
  974. {
  975. interface RedirectableUrlMatcherInterface
  976. {
  977. public function redirect($path, $route, $scheme = null);
  978. }
  979. }
  980. namespace Symfony\Component\Routing\Matcher
  981. {
  982. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  983. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  984. use Symfony\Component\Routing\RouteCollection;
  985. use Symfony\Component\Routing\RequestContext;
  986. use Symfony\Component\Routing\Route;
  987. class UrlMatcher implements UrlMatcherInterface
  988. {
  989. const REQUIREMENT_MATCH = 0;
  990. const REQUIREMENT_MISMATCH = 1;
  991. const ROUTE_MATCH = 2;
  992. protected $context;
  993. protected $allow;
  994. private $routes;
  995. public function __construct(RouteCollection $routes, RequestContext $context)
  996. {
  997. $this->routes = $routes;
  998. $this->context = $context;
  999. }
  1000. public function setContext(RequestContext $context)
  1001. {
  1002. $this->context = $context;
  1003. }
  1004. public function getContext()
  1005. {
  1006. return $this->context;
  1007. }
  1008. public function match($pathinfo)
  1009. {
  1010. $this->allow = array();
  1011. if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) {
  1012. return $ret;
  1013. }
  1014. throw 0 < count($this->allow)
  1015. ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
  1016. : new ResourceNotFoundException();
  1017. }
  1018. protected function matchCollection($pathinfo, RouteCollection $routes)
  1019. {
  1020. foreach ($routes as $name => $route) {
  1021. if ($route instanceof RouteCollection) {
  1022. if (false === strpos($route->getPrefix(), '{') && $route->getPrefix() !== substr($pathinfo, 0, strlen($route->getPrefix()))) {
  1023. continue;
  1024. }
  1025. if (!$ret = $this->matchCollection($pathinfo, $route)) {
  1026. continue;
  1027. }
  1028. return $ret;
  1029. }
  1030. $compiledRoute = $route->compile();
  1031. if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
  1032. continue;
  1033. }
  1034. if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
  1035. continue;
  1036. }
  1037. if ($req = $route->getRequirement('_method')) {
  1038. if ('HEAD' === $method = $this->context->getMethod()) {
  1039. $method = 'GET';
  1040. }
  1041. if (!in_array($method, $req = explode('|', strtoupper($req)))) {
  1042. $this->allow = array_merge($this->allow, $req);
  1043. continue;
  1044. }
  1045. }
  1046. $status = $this->handleRouteRequirements($pathinfo, $name, $route);
  1047. if (self::ROUTE_MATCH === $status[0]) {
  1048. return $status[1];
  1049. }
  1050. if (self::REQUIREMENT_MISMATCH === $status[0]) {
  1051. continue;
  1052. }
  1053. return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
  1054. }
  1055. }
  1056. protected function handleRouteRequirements($pathinfo, $name, Route $route)
  1057. {
  1058. $scheme = $route->getRequirement('_scheme');
  1059. $status = $scheme && $scheme !== $this->context->getScheme() ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
  1060. return array($status, null);
  1061. }
  1062. protected function mergeDefaults($params, $defaults)
  1063. {
  1064. foreach ($params as $key => $value) {
  1065. if (!is_int($key)) {
  1066. $defaults[$key] = $value;
  1067. }
  1068. }
  1069. return $defaults;
  1070. }
  1071. }
  1072. }
  1073. namespace Symfony\Component\Routing\Matcher
  1074. {
  1075. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  1076. use Symfony\Component\Routing\Route;
  1077. abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
  1078. {
  1079. public function match($pathinfo)
  1080. {
  1081. try {
  1082. $parameters = parent::match($pathinfo);
  1083. } catch (ResourceNotFoundException $e) {
  1084. if ('/' === substr($pathinfo, -1) || !in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
  1085. throw $e;
  1086. }
  1087. try {
  1088. parent::match($pathinfo.'/');
  1089. return $this->redirect($pathinfo.'/', null);
  1090. } catch (ResourceNotFoundException $e2) {
  1091. throw $e;
  1092. }
  1093. }
  1094. return $parameters;
  1095. }
  1096. protected function handleRouteRequirements($pathinfo, $name, Route $route)
  1097. {
  1098. $scheme = $route->getRequirement('_scheme');
  1099. if ($scheme && $this->context->getScheme() !== $scheme) {
  1100. return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, $scheme));
  1101. }
  1102. return array(self::REQUIREMENT_MATCH, null);
  1103. }
  1104. }
  1105. }
  1106. namespace Symfony\Bundle\FrameworkBundle\Routing
  1107. {
  1108. use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseMatcher;
  1109. class RedirectableUrlMatcher extends BaseMatcher
  1110. {
  1111. public function redirect($path, $route, $scheme = null)
  1112. {
  1113. return array(
  1114. '_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction',
  1115. 'path' => $path,
  1116. 'permanent' => true,
  1117. 'scheme' => $scheme,
  1118. 'httpPort' => $this->context->getHttpPort(),
  1119. 'httpsPort' => $this->context->getHttpsPort(),
  1120. '_route' => $route,
  1121. );
  1122. }
  1123. }
  1124. }
  1125. namespace Symfony\Component\HttpKernel\CacheWarmer
  1126. {
  1127. interface WarmableInterface
  1128. {
  1129. public function warmUp($cacheDir);
  1130. }
  1131. }
  1132. namespace Symfony\Bundle\FrameworkBundle\Routing
  1133. {
  1134. use Symfony\Component\Routing\Router as BaseRouter;
  1135. use Symfony\Component\Routing\RequestContext;
  1136. use Symfony\Component\DependencyInjection\ContainerInterface;
  1137. use Symfony\Component\Routing\RouteCollection;
  1138. use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
  1139. use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  1140. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  1141. class Router extends BaseRouter implements WarmableInterface
  1142. {
  1143. private $container;
  1144. public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null)
  1145. {
  1146. $this->container = $container;
  1147. $this->resource = $resource;
  1148. $this->context = null === $context ? new RequestContext() : $context;
  1149. $this->setOptions($options);
  1150. }
  1151. public function getRouteCollection()
  1152. {
  1153. if (null === $this->collection) {
  1154. $this->collection = $this->container->get('routing.loader')->load($this->resource, $this->options['resource_type']);
  1155. $this->resolveParameters($this->collection);
  1156. }
  1157. return $this->collection;
  1158. }
  1159. public function warmUp($cacheDir)
  1160. {
  1161. $currentDir = $this->getOption('cache_dir');
  1162. $this->setOption('cache_dir', $cacheDir);
  1163. $this->getMatcher();
  1164. $this->getGenerator();
  1165. $this->setOption('cache_dir', $currentDir);
  1166. }
  1167. private function resolveParameters(RouteCollection $collection)
  1168. {
  1169. foreach ($collection as $route) {
  1170. if ($route instanceof RouteCollection) {
  1171. $this->resolveParameters($route);
  1172. } else {
  1173. foreach ($route->getDefaults() as $name => $value) {
  1174. $route->setDefault($name, $this->resolveString($value));
  1175. }
  1176. foreach ($route->getRequirements() as $name => $value) {
  1177. $route->setRequirement($name, $this->resolveString($value));
  1178. }
  1179. $route->setPattern($this->resolveString($route->getPattern()));
  1180. }
  1181. }
  1182. }
  1183. private function resolveString($value)
  1184. {
  1185. $container = $this->container;
  1186. if (null === $value || false === $value || true === $value || is_object($value) || is_array($value)) {
  1187. return $value;
  1188. }
  1189. $escapedValue = preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($container, $value) {
  1190. if (!isset($match[1])) {
  1191. return '%%';
  1192. }
  1193. $key = strtolower($match[1]);
  1194. if (!$container->hasParameter($key)) {
  1195. throw new ParameterNotFoundException($key);
  1196. }
  1197. $resolved = $container->getParameter($key);
  1198. if (is_string($resolved) || is_numeric($resolved)) {
  1199. return (string) $resolved;
  1200. }
  1201. throw new RuntimeException(sprintf(
  1202. 'A string value must be composed of strings and/or numbers,' .
  1203. 'but found parameter "%s" of type %s inside string value "%s".',
  1204. $key,
  1205. gettype($resolved),
  1206. $value)
  1207. );
  1208. }, $value);
  1209. return str_replace('%%', '%', $escapedValue);
  1210. }
  1211. }
  1212. }
  1213. namespace Symfony\Bundle\FrameworkBundle\Templating
  1214. {
  1215. use Symfony\Component\DependencyInjection\ContainerInterface;
  1216. class GlobalVariables
  1217. {
  1218. protected $container;
  1219. public function __construct(ContainerInterface $container)
  1220. {
  1221. $this->container = $container;
  1222. }
  1223. public function getSecurity()
  1224. {
  1225. if ($this->container->has('security.context')) {
  1226. return $this->container->get('security.context');
  1227. }
  1228. }
  1229. public function getUser()
  1230. {
  1231. if (!$security = $this->getSecurity()) {
  1232. return;
  1233. }
  1234. if (!$token = $security->getToken()) {
  1235. return;
  1236. }
  1237. $user = $token->getUser();
  1238. if (!is_object($user)) {
  1239. return;
  1240. }
  1241. return $user;
  1242. }
  1243. public function getRequest()
  1244. {
  1245. if ($this->container->has('request') && $request = $this->container->get('request')) {
  1246. return $request;
  1247. }
  1248. }
  1249. public function getSession()
  1250. {
  1251. if ($request = $this->getRequest()) {
  1252. return $request->getSession();
  1253. }
  1254. }
  1255. public function getEnvironment()
  1256. {
  1257. return $this->container->getParameter('kernel.environment');
  1258. }
  1259. public function getDebug()
  1260. {
  1261. return (Boolean) $this->container->getParameter('kernel.debug');
  1262. }
  1263. }
  1264. }
  1265. namespace Symfony\Component\Templating
  1266. {
  1267. interface TemplateReferenceInterface
  1268. {
  1269. public function all();
  1270. public function set($name, $value);
  1271. public function get($name);
  1272. public function getPath();
  1273. public function getLogicalName();
  1274. }
  1275. }
  1276. namespace Symfony\Component\Templating
  1277. {
  1278. class TemplateReference implements TemplateReferenceInterface
  1279. {
  1280. protected $parameters;
  1281. public function __construct($name = null, $engine = null)
  1282. {
  1283. $this->parameters = array(
  1284. 'name' => $name,
  1285. 'engine' => $engine,
  1286. );
  1287. }
  1288. public function __toString()
  1289. {
  1290. return $this->getLogicalName();
  1291. }
  1292. public function set($name, $value)
  1293. {
  1294. if (array_key_exists($name, $this->parameters)) {
  1295. $this->parameters[$name] = $value;
  1296. } else {
  1297. throw new \InvalidArgumentException(sprintf('The template does not support the "%s" parameter.', $name));
  1298. }
  1299. return $this;
  1300. }
  1301. public function get($name)
  1302. {
  1303. if (array_key_exists($name, $this->parameters)) {
  1304. return $this->parameters[$name];
  1305. }
  1306. throw new \InvalidArgumentException(sprintf('The template does not support the "%s" parameter.', $name));
  1307. }
  1308. public function all()
  1309. {
  1310. return $this->parameters;
  1311. }
  1312. public function getPath()
  1313. {
  1314. return $this->parameters['name'];
  1315. }
  1316. public function getLogicalName()
  1317. {
  1318. return $this->parameters['name'];
  1319. }
  1320. }
  1321. }
  1322. namespace Symfony\Bundle\FrameworkBundle\Templating
  1323. {
  1324. use Symfony\Component\Templating\TemplateReference as BaseTemplateReference;
  1325. class TemplateReference extends BaseTemplateReference
  1326. {
  1327. public function __construct($bundle = null, $controller = null, $name = null, $format = null, $engine = null)
  1328. {
  1329. $this->parameters = array(
  1330. 'bundle' => $bundle,
  1331. 'controller' => $controller,
  1332. 'name' => $name,
  1333. 'format' => $format,
  1334. 'engine' => $engine,
  1335. );
  1336. }
  1337. public function getPath()
  1338. {
  1339. $controller = str_replace('\\', '/', $this->get('controller'));
  1340. $path = (empty($controller) ? '' : $controller.'/').$this->get('name').'.'.$this->get('format').'.'.$this->get('engine');
  1341. return empty($this->parameters['bundle']) ? 'views/'.$path : '@'.$this->get('bundle').'/Resources/views/'.$path;
  1342. }
  1343. public function getLogicalName()
  1344. {
  1345. return sprintf('%s:%s:%s.%s.%s', $this->parameters['bundle'], $this->parameters['controller'], $this->parameters['name'], $this->parameters['format'], $this->parameters['engine']);
  1346. }
  1347. }
  1348. }
  1349. namespace Symfony\Component\Templating
  1350. {
  1351. interface TemplateNameParserInterface
  1352. {
  1353. public function parse($name);
  1354. }
  1355. }
  1356. namespace Symfony\Bundle\FrameworkBundle\Templating
  1357. {
  1358. use Symfony\Component\Templating\TemplateNameParserInterface;
  1359. use Symfony\Component\Templating\TemplateReferenceInterface;
  1360. use Symfony\Component\HttpKernel\KernelInterface;
  1361. class TemplateNameParser implements TemplateNameParserInterface
  1362. {
  1363. protected $kernel;
  1364. protected $cache;
  1365. public function __construct(KernelInterface $kernel)
  1366. {
  1367. $this->kernel = $kernel;
  1368. $this->cache = array();
  1369. }
  1370. public function parse($name)
  1371. {
  1372. if ($name instanceof TemplateReferenceInterface) {
  1373. return $name;
  1374. } elseif (isset($this->cache[$name])) {
  1375. return $this->cache[$name];
  1376. }
  1377. $name = str_replace(':/', ':', preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')));
  1378. if (false !== strpos($name, '..')) {
  1379. throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name));
  1380. }
  1381. $parts = explode(':', $name);
  1382. if (3 !== count($parts)) {
  1383. throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.format.engine").', $name));
  1384. }
  1385. $elements = explode('.', $parts[2]);
  1386. if (3 > count($elements)) {
  1387. throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.format.engine").', $name));
  1388. }
  1389. $engine = array_pop($elements);
  1390. $format = array_pop($elements);
  1391. $template = new TemplateReference($parts[0], $parts[1], implode('.', $elements), $format, $engine);
  1392. if ($template->get('bundle')) {
  1393. try {
  1394. $this->kernel->getBundle($template->get('bundle'));
  1395. } catch (\Exception $e) {
  1396. throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid.', $name), 0, $e);
  1397. }
  1398. }
  1399. return $this->cache[$name] = $template;
  1400. }
  1401. }
  1402. }
  1403. namespace Symfony\Component\Config
  1404. {
  1405. interface FileLocatorInterface
  1406. {
  1407. public function locate($name, $currentPath = null, $first = true);
  1408. }
  1409. }
  1410. namespace Symfony\Bundle\FrameworkBundle\Templating\Loader
  1411. {
  1412. use Symfony\Component\Config\FileLocatorInterface;
  1413. use Symfony\Component\Templating\TemplateReferenceInterface;
  1414. class TemplateLocator implements FileLocatorInterface
  1415. {
  1416. protected $locator;
  1417. protected $cache;
  1418. public function __construct(FileLocatorInterface $locator, $cacheDir = null)
  1419. {
  1420. if (null !== $cacheDir && is_file($cache = $cacheDir.'/templates.php')) {
  1421. $this->cache = require $cache;
  1422. }
  1423. $this->locator = $locator;
  1424. }
  1425. protected function getCacheKey($template)
  1426. {
  1427. return $template->getLogicalName();
  1428. }
  1429. public function locate($template, $currentPath = null, $first = true)
  1430. {
  1431. if (!$template instanceof TemplateReferenceInterface) {
  1432. throw new \InvalidArgumentException("The template must be an instance of TemplateReferenceInterface.");
  1433. }
  1434. $key = $this->getCacheKey($template);
  1435. if (isset($this->cache[$key])) {
  1436. return $this->cache[$key];
  1437. }
  1438. try {
  1439. return $this->cache[$key] = $this->locator->locate($template->getPath(), $currentPath);
  1440. } catch (\InvalidArgumentException $e) {
  1441. throw new \InvalidArgumentException(sprintf('Unable to find template "%s" : "%s".', $template, $e->getMessage()), 0, $e);
  1442. }
  1443. }
  1444. }
  1445. }
  1446. namespace Symfony\Component\HttpFoundation
  1447. {
  1448. class ParameterBag implements \IteratorAggregate, \Countable
  1449. {
  1450. protected $parameters;
  1451. public function __construct(array $parameters = array())
  1452. {
  1453. $this->parameters = $parameters;
  1454. }
  1455. public function all()
  1456. {
  1457. return $this->parameters;
  1458. }
  1459. public function keys()
  1460. {
  1461. return array_keys($this->parameters);
  1462. }
  1463. public function replace(array $parameters = array())
  1464. {
  1465. $this->parameters = $parameters;
  1466. }
  1467. public function add(array $parameters = array())
  1468. {
  1469. $this->parameters = array_replace($this->parameters, $parameters);
  1470. }
  1471. public function get($path, $default = null, $deep = false)
  1472. {
  1473. if (!$deep || false === $pos = strpos($path, '[')) {
  1474. return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default;
  1475. }
  1476. $root = substr($path, 0, $pos);
  1477. if (!array_key_exists($root, $this->parameters)) {
  1478. return $default;
  1479. }
  1480. $value = $this->parameters[$root];
  1481. $currentKey = null;
  1482. for ($i = $pos, $c = strlen($path); $i < $c; $i++) {
  1483. $char = $path[$i];
  1484. if ('[' === $char) {
  1485. if (null !== $currentKey) {
  1486. throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i));
  1487. }
  1488. $currentKey = '';
  1489. } elseif (']' === $char) {
  1490. if (null === $currentKey) {
  1491. throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i));
  1492. }
  1493. if (!is_array($value) || !array_key_exists($currentKey, $value)) {
  1494. return $default;
  1495. }
  1496. $value = $value[$currentKey];
  1497. $currentKey = null;
  1498. } else {
  1499. if (null === $currentKey) {
  1500. throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i));
  1501. }
  1502. $currentKey .= $char;
  1503. }
  1504. }
  1505. if (null !== $currentKey) {
  1506. throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".'));
  1507. }
  1508. return $value;
  1509. }
  1510. public function set($key, $value)
  1511. {
  1512. $this->parameters[$key] = $value;
  1513. }
  1514. public function has($key)
  1515. {
  1516. return array_key_exists($key, $this->parameters);
  1517. }
  1518. public function remove($key)
  1519. {
  1520. unset($this->parameters[$key]);
  1521. }
  1522. public function getAlpha($key, $default = '', $deep = false)
  1523. {
  1524. return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep));
  1525. }
  1526. public function getAlnum($key, $default = '', $deep = false)
  1527. {
  1528. return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep));
  1529. }
  1530. public function getDigits($key, $default = '', $deep = false)
  1531. {
  1532. return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT));
  1533. }
  1534. public function getInt($key, $default = 0, $deep = false)
  1535. {
  1536. return (int) $this->get($key, $default, $deep);
  1537. }
  1538. public function filter($key, $default = null, $deep = false, $filter=FILTER_DEFAULT, $options=array())
  1539. {
  1540. $value = $this->get($key, $default, $deep);
  1541. if (!is_array($options) && $options) {
  1542. $options = array('flags' => $options);
  1543. }
  1544. if (is_array($value) && !isset($options['flags'])) {
  1545. $options['flags'] = FILTER_REQUIRE_ARRAY;
  1546. }
  1547. return filter_var($value, $filter, $options);
  1548. }
  1549. public function getIterator()
  1550. {
  1551. return new \ArrayIterator($this->parameters);
  1552. }
  1553. public function count()
  1554. {
  1555. return count($this->parameters);
  1556. }
  1557. }
  1558. }
  1559. namespace Symfony\Component\HttpFoundation
  1560. {
  1561. class HeaderBag implements \IteratorAggregate, \Countable
  1562. {
  1563. protected $headers;
  1564. protected $cacheControl;
  1565. public function __construct(array $headers = array())
  1566. {
  1567. $this->cacheControl = array();
  1568. $this->headers = array();
  1569. foreach ($headers as $key => $values) {
  1570. $this->set($key, $values);
  1571. }
  1572. }
  1573. public function __toString()
  1574. {
  1575. if (!$this->headers) {
  1576. return '';
  1577. }
  1578. $max = max(array_map('strlen', array_keys($this->headers))) + 1;
  1579. $content = '';
  1580. ksort($this->headers);
  1581. foreach ($this->headers as $name => $values) {
  1582. $name = implode('-', array_map('ucfirst', explode('-', $name)));
  1583. foreach ($values as $value) {
  1584. $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
  1585. }
  1586. }
  1587. return $content;
  1588. }
  1589. public function all()
  1590. {
  1591. return $this->headers;
  1592. }
  1593. public function keys()
  1594. {
  1595. return array_keys($this->headers);
  1596. }
  1597. public function replace(array $headers = array())
  1598. {
  1599. $this->headers = array();
  1600. $this->add($headers);
  1601. }
  1602. public function add(array $headers)
  1603. {
  1604. foreach ($headers as $key => $values) {
  1605. $this->set($key, $values);
  1606. }
  1607. }
  1608. public function get($key, $default = null, $first = true)
  1609. {
  1610. $key = strtr(strtolower($key), '_', '-');
  1611. if (!array_key_exists($key, $this->headers)) {
  1612. if (null === $default) {
  1613. return $first ? null : array();
  1614. }
  1615. return $first ? $default : array($default);
  1616. }
  1617. if ($first) {
  1618. return count($this->headers[$key]) ? $this->headers[$key][0] : $default;
  1619. }
  1620. return $this->headers[$key];
  1621. }
  1622. public function set($key, $values, $replace = true)
  1623. {
  1624. $key = strtr(strtolower($key), '_', '-');
  1625. $values = array_values((array) $values);
  1626. if (true === $replace || !isset($this->headers[$key])) {
  1627. $this->headers[$key] = $values;
  1628. } else {
  1629. $this->headers[$key] = array_merge($this->headers[$key], $values);
  1630. }
  1631. if ('cache-control' === $key) {
  1632. $this->cacheControl = $this->parseCacheControl($values[0]);
  1633. }
  1634. }
  1635. public function has($key)
  1636. {
  1637. return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers);
  1638. }
  1639. public function contains($key, $value)
  1640. {
  1641. return in_array($value, $this->get($key, null, false));
  1642. }
  1643. public function remove($key)
  1644. {
  1645. $key = strtr(strtolower($key), '_', '-');
  1646. unset($this->headers[$key]);
  1647. if ('cache-control' === $key) {
  1648. $this->cacheControl = array();
  1649. }
  1650. }
  1651. public function getDate($key, \DateTime $default = null)
  1652. {
  1653. if (null === $value = $this->get($key)) {
  1654. return $default;
  1655. }
  1656. if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
  1657. throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
  1658. }
  1659. return $date;
  1660. }
  1661. public function addCacheControlDirective($key, $value = true)
  1662. {
  1663. $this->cacheControl[$key] = $value;
  1664. $this->set('Cache-Control', $this->getCacheControlHeader());
  1665. }
  1666. public function hasCacheControlDirective($key)
  1667. {
  1668. return array_key_exists($key, $this->cacheControl);
  1669. }
  1670. public function getCacheControlDirective($key)
  1671. {
  1672. return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
  1673. }
  1674. public function removeCacheControlDirective($key)
  1675. {
  1676. unset($this->cacheControl[$key]);
  1677. $this->set('Cache-Control', $this->getCacheControlHeader());
  1678. }
  1679. public function getIterator()
  1680. {
  1681. return new \ArrayIterator($this->headers);
  1682. }
  1683. public function count()
  1684. {
  1685. return count($this->headers);
  1686. }
  1687. protected function getCacheControlHeader()
  1688. {
  1689. $parts = array();
  1690. ksort($this->cacheControl);
  1691. foreach ($this->cacheControl as $key => $value) {
  1692. if (true === $value) {
  1693. $parts[] = $key;
  1694. } else {
  1695. if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
  1696. $value = '"'.$value.'"';
  1697. }
  1698. $parts[] = "$key=$value";
  1699. }
  1700. }
  1701. return implode(', ', $parts);
  1702. }
  1703. protected function parseCacheControl($header)
  1704. {
  1705. $cacheControl = array();
  1706. preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
  1707. foreach ($matches as $match) {
  1708. $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
  1709. }
  1710. return $cacheControl;
  1711. }
  1712. }
  1713. }
  1714. namespace Symfony\Component\HttpFoundation
  1715. {
  1716. use Symfony\Component\HttpFoundation\File\UploadedFile;
  1717. class FileBag extends ParameterBag
  1718. {
  1719. private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
  1720. public function __construct(array $parameters = array())
  1721. {
  1722. $this->replace($parameters);
  1723. }
  1724. public function replace(array $files = array())
  1725. {
  1726. $this->parameters = array();
  1727. $this->add($files);
  1728. }
  1729. public function set($key, $value)
  1730. {
  1731. if (!is_array($value) && !$value instanceof UploadedFile) {
  1732. throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
  1733. }
  1734. parent::set($key, $this->convertFileInformation($value));
  1735. }
  1736. public function add(array $files = array())
  1737. {
  1738. foreach ($files as $key => $file) {
  1739. $this->set($key, $file);
  1740. }
  1741. }
  1742. protected function convertFileInformation($file)
  1743. {
  1744. if ($file instanceof UploadedFile) {
  1745. return $file;
  1746. }
  1747. $file = $this->fixPhpFilesArray($file);
  1748. if (is_array($file)) {
  1749. $keys = array_keys($file);
  1750. sort($keys);
  1751. if ($keys == self::$fileKeys) {
  1752. if (UPLOAD_ERR_NO_FILE == $file['error']) {
  1753. $file = null;
  1754. } else {
  1755. $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
  1756. }
  1757. } else {
  1758. $file = array_map(array($this, 'convertFileInformation'), $file);
  1759. }
  1760. }
  1761. return $file;
  1762. }
  1763. protected function fixPhpFilesArray($data)
  1764. {
  1765. if (!is_array($data)) {
  1766. return $data;
  1767. }
  1768. $keys = array_keys($data);
  1769. sort($keys);
  1770. if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
  1771. return $data;
  1772. }
  1773. $files = $data;
  1774. foreach (self::$fileKeys as $k) {
  1775. unset($files[$k]);
  1776. }
  1777. foreach (array_keys($data['name']) as $key) {
  1778. $files[$key] = $this->fixPhpFilesArray(array(
  1779. 'error' => $data['error'][$key],
  1780. 'name' => $data['name'][$key],
  1781. 'type' => $data['type'][$key],
  1782. 'tmp_name' => $data['tmp_name'][$key],
  1783. 'size' => $data['size'][$key]
  1784. ));
  1785. }
  1786. return $files;
  1787. }
  1788. }
  1789. }
  1790. namespace Symfony\Component\HttpFoundation
  1791. {
  1792. class ServerBag extends ParameterBag
  1793. {
  1794. public function getHeaders()
  1795. {
  1796. $headers = array();
  1797. foreach ($this->parameters as $key => $value) {
  1798. if (0 === strpos($key, 'HTTP_')) {
  1799. $headers[substr($key, 5)] = $value;
  1800. }
  1801. elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) {
  1802. $headers[$key] = $value;
  1803. }
  1804. }
  1805. if (isset($this->parameters['PHP_AUTH_USER'])) {
  1806. $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
  1807. $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
  1808. } else {
  1809. $authorizationHeader = null;
  1810. if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
  1811. $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
  1812. } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
  1813. $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
  1814. }
  1815. if ((null !== $authorizationHeader) && (0 === stripos($authorizationHeader, 'basic'))) {
  1816. $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)));
  1817. if (count($exploded) == 2) {
  1818. list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
  1819. }
  1820. }
  1821. }
  1822. if (isset($headers['PHP_AUTH_USER'])) {
  1823. $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
  1824. }
  1825. return $headers;
  1826. }
  1827. }
  1828. }
  1829. namespace Symfony\Component\HttpFoundation
  1830. {
  1831. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  1832. class Request
  1833. {
  1834. const HEADER_CLIENT_IP = 'client_ip';
  1835. const HEADER_CLIENT_HOST = 'client_host';
  1836. const HEADER_CLIENT_PROTO = 'client_proto';
  1837. const HEADER_CLIENT_PORT = 'client_port';
  1838. protected static $trustProxy = false;
  1839. protected static $trustedProxies = array();
  1840. protected static $trustedHeaders = array(
  1841. self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
  1842. self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
  1843. self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
  1844. self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
  1845. );
  1846. public $attributes;
  1847. public $request;
  1848. public $query;
  1849. public $server;
  1850. public $files;
  1851. public $cookies;
  1852. public $headers;
  1853. protected $content;
  1854. protected $languages;
  1855. protected $charsets;
  1856. protected $acceptableContentTypes;
  1857. protected $pathInfo;
  1858. protected $requestUri;
  1859. protected $baseUrl;
  1860. protected $basePath;
  1861. protected $method;
  1862. protected $format;
  1863. protected $session;
  1864. protected $locale;
  1865. protected $defaultLocale = 'en';
  1866. protected static $formats;
  1867. public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
  1868. {
  1869. $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
  1870. }
  1871. public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
  1872. {
  1873. $this->request = new ParameterBag($request);
  1874. $this->query = new ParameterBag($query);
  1875. $this->attributes = new ParameterBag($attributes);
  1876. $this->cookies = new ParameterBag($cookies);
  1877. $this->files = new FileBag($files);
  1878. $this->server = new ServerBag($server);
  1879. $this->headers = new HeaderBag($this->server->getHeaders());
  1880. $this->content = $content;
  1881. $this->languages = null;
  1882. $this->charsets = null;
  1883. $this->acceptableContentTypes = null;
  1884. $this->pathInfo = null;
  1885. $this->requestUri = null;
  1886. $this->baseUrl = null;
  1887. $this->basePath = null;
  1888. $this->method = null;
  1889. $this->format = null;
  1890. }
  1891. public static function createFromGlobals()
  1892. {
  1893. $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
  1894. if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
  1895. && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
  1896. ) {
  1897. parse_str($request->getContent(), $data);
  1898. $request->request = new ParameterBag($data);
  1899. }
  1900. return $request;
  1901. }
  1902. public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
  1903. {
  1904. $defaults = array(
  1905. 'SERVER_NAME' => 'localhost',
  1906. 'SERVER_PORT' => 80,
  1907. 'HTTP_HOST' => 'localhost',
  1908. 'HTTP_USER_AGENT' => 'Symfony/2.X',
  1909. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  1910. 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
  1911. 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
  1912. 'REMOTE_ADDR' => '127.0.0.1',
  1913. 'SCRIPT_NAME' => '',
  1914. 'SCRIPT_FILENAME' => '',
  1915. 'SERVER_PROTOCOL' => 'HTTP/1.1',
  1916. 'REQUEST_TIME' => time(),
  1917. );
  1918. $components = parse_url($uri);
  1919. if (isset($components['host'])) {
  1920. $defaults['SERVER_NAME'] = $components['host'];
  1921. $defaults['HTTP_HOST'] = $components['host'];
  1922. }
  1923. if (isset($components['scheme'])) {
  1924. if ('https' === $components['scheme']) {
  1925. $defaults['HTTPS'] = 'on';
  1926. $defaults['SERVER_PORT'] = 443;
  1927. }
  1928. }
  1929. if (isset($components['port'])) {
  1930. $defaults['SERVER_PORT'] = $components['port'];
  1931. $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port'];
  1932. }
  1933. if (isset($components['user'])) {
  1934. $defaults['PHP_AUTH_USER'] = $components['user'];
  1935. }
  1936. if (isset($components['pass'])) {
  1937. $defaults['PHP_AUTH_PW'] = $components['pass'];
  1938. }
  1939. if (!isset($components['path'])) {
  1940. $components['path'] = '/';
  1941. }
  1942. switch (strtoupper($method)) {
  1943. case 'POST':
  1944. case 'PUT':
  1945. case 'DELETE':
  1946. $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
  1947. case 'PATCH':
  1948. $request = $parameters;
  1949. $query = array();
  1950. break;
  1951. default:
  1952. $request = array();
  1953. $query = $parameters;
  1954. break;
  1955. }
  1956. if (isset($components['query'])) {
  1957. parse_str(html_entity_decode($components['query']), $qs);
  1958. $query = array_replace($qs, $query);
  1959. }
  1960. $queryString = http_build_query($query, '', '&');
  1961. $uri = $components['path'].('' !== $queryString ? '?'.$queryString : '');
  1962. $server = array_replace($defaults, $server, array(
  1963. 'REQUEST_METHOD' => strtoupper($method),
  1964. 'PATH_INFO' => '',
  1965. 'REQUEST_URI' => $uri,
  1966. 'QUERY_STRING' => $queryString,
  1967. ));
  1968. return new static($query, $request, array(), $cookies, $files, $server, $content);
  1969. }
  1970. public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
  1971. {
  1972. $dup = clone $this;
  1973. if ($query !== null) {
  1974. $dup->query = new ParameterBag($query);
  1975. }
  1976. if ($request !== null) {
  1977. $dup->request = new ParameterBag($request);
  1978. }
  1979. if ($attributes !== null) {
  1980. $dup->attributes = new ParameterBag($attributes);
  1981. }
  1982. if ($cookies !== null) {
  1983. $dup->cookies = new ParameterBag($cookies);
  1984. }
  1985. if ($files !== null) {
  1986. $dup->files = new FileBag($files);
  1987. }
  1988. if ($server !== null) {
  1989. $dup->server = new ServerBag($server);
  1990. $dup->headers = new HeaderBag($dup->server->getHeaders());
  1991. }
  1992. $dup->languages = null;
  1993. $dup->charsets = null;
  1994. $dup->acceptableContentTypes = null;
  1995. $dup->pathInfo = null;
  1996. $dup->requestUri = null;
  1997. $dup->baseUrl = null;
  1998. $dup->basePath = null;
  1999. $dup->method = null;
  2000. $dup->format = null;
  2001. return $dup;
  2002. }
  2003. public function __clone()
  2004. {
  2005. $this->query = clone $this->query;
  2006. $this->request = clone $this->request;
  2007. $this->attributes = clone $this->attributes;
  2008. $this->cookies = clone $this->cookies;
  2009. $this->files = clone $this->files;
  2010. $this->server = clone $this->server;
  2011. $this->headers = clone $this->headers;
  2012. }
  2013. public function __toString()
  2014. {
  2015. return
  2016. sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
  2017. $this->headers."\r\n".
  2018. $this->getContent();
  2019. }
  2020. public function overrideGlobals()
  2021. {
  2022. $_GET = $this->query->all();
  2023. $_POST = $this->request->all();
  2024. $_SERVER = $this->server->all();
  2025. $_COOKIE = $this->cookies->all();
  2026. foreach ($this->headers->all() as $key => $value) {
  2027. $key = strtoupper(str_replace('-', '_', $key));
  2028. if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
  2029. $_SERVER[$key] = implode(', ', $value);
  2030. } else {
  2031. $_SERVER['HTTP_'.$key] = implode(', ', $value);
  2032. }
  2033. }
  2034. $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
  2035. $requestOrder = ini_get('request_order') ?: ini_get('variable_order');
  2036. $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
  2037. $_REQUEST = array();
  2038. foreach (str_split($requestOrder) as $order) {
  2039. $_REQUEST = array_merge($_REQUEST, $request[$order]);
  2040. }
  2041. }
  2042. public static function trustProxyData()
  2043. {
  2044. self::$trustProxy = true;
  2045. }
  2046. public static function setTrustedProxies(array $proxies)
  2047. {
  2048. self::$trustedProxies = $proxies;
  2049. self::$trustProxy = $proxies ? true : false;
  2050. }
  2051. public static function setTrustedHeaderName($key, $value)
  2052. {
  2053. if (!array_key_exists($key, self::$trustedHeaders)) {
  2054. throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
  2055. }
  2056. self::$trustedHeaders[$key] = $value;
  2057. }
  2058. public static function isProxyTrusted()
  2059. {
  2060. return self::$trustProxy;
  2061. }
  2062. public static function normalizeQueryString($qs)
  2063. {
  2064. if ('' == $qs) {
  2065. return '';
  2066. }
  2067. $parts = array();
  2068. $order = array();
  2069. foreach (explode('&', $qs) as $param) {
  2070. if ('' === $param || '=' === $param[0]) {
  2071. continue;
  2072. }
  2073. $keyValuePair = explode('=', $param, 2);
  2074. $parts[] = isset($keyValuePair[1]) ?
  2075. rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
  2076. rawurlencode(urldecode($keyValuePair[0]));
  2077. $order[] = urldecode($keyValuePair[0]);
  2078. }
  2079. array_multisort($order, SORT_ASC, $parts);
  2080. return implode('&', $parts);
  2081. }
  2082. public function get($key, $default = null, $deep = false)
  2083. {
  2084. return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep);
  2085. }
  2086. public function getSession()
  2087. {
  2088. return $this->session;
  2089. }
  2090. public function hasPreviousSession()
  2091. {
  2092. return $this->hasSession() && $this->cookies->has($this->session->getName());
  2093. }
  2094. public function hasSession()
  2095. {
  2096. return null !== $this->session;
  2097. }
  2098. public function setSession(SessionInterface $session)
  2099. {
  2100. $this->session = $session;
  2101. }
  2102. public function getClientIp()
  2103. {
  2104. $ip = $this->server->get('REMOTE_ADDR');
  2105. if (!self::$trustProxy) {
  2106. return $ip;
  2107. }
  2108. if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
  2109. return $ip;
  2110. }
  2111. $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
  2112. $clientIps[] = $ip;
  2113. $trustedProxies = self::$trustProxy && !self::$trustedProxies ? array($ip) : self::$trustedProxies;
  2114. $clientIps = array_diff($clientIps, $trustedProxies);
  2115. return array_pop($clientIps);
  2116. }
  2117. public function getScriptName()
  2118. {
  2119. return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
  2120. }
  2121. public function getPathInfo()
  2122. {
  2123. if (null === $this->pathInfo) {
  2124. $this->pathInfo = $this->preparePathInfo();
  2125. }
  2126. return $this->pathInfo;
  2127. }
  2128. public function getBasePath()
  2129. {
  2130. if (null === $this->basePath) {
  2131. $this->basePath = $this->prepareBasePath();
  2132. }
  2133. return $this->basePath;
  2134. }
  2135. public function getBaseUrl()
  2136. {
  2137. if (null === $this->baseUrl) {
  2138. $this->baseUrl = $this->prepareBaseUrl();
  2139. }
  2140. return $this->baseUrl;
  2141. }
  2142. public function getScheme()
  2143. {
  2144. return $this->isSecure() ? 'https' : 'http';
  2145. }
  2146. public function getPort()
  2147. {
  2148. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) {
  2149. return $port;
  2150. }
  2151. return $this->server->get('SERVER_PORT');
  2152. }
  2153. public function getUser()
  2154. {
  2155. return $this->server->get('PHP_AUTH_USER');
  2156. }
  2157. public function getPassword()
  2158. {
  2159. return $this->server->get('PHP_AUTH_PW');
  2160. }
  2161. public function getUserInfo()
  2162. {
  2163. $userinfo = $this->getUser();
  2164. $pass = $this->getPassword();
  2165. if ('' != $pass) {
  2166. $userinfo .= ":$pass";
  2167. }
  2168. return $userinfo;
  2169. }
  2170. public function getHttpHost()
  2171. {
  2172. $scheme = $this->getScheme();
  2173. $port = $this->getPort();
  2174. if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
  2175. return $this->getHost();
  2176. }
  2177. return $this->getHost().':'.$port;
  2178. }
  2179. public function getRequestUri()
  2180. {
  2181. if (null === $this->requestUri) {
  2182. $this->requestUri = $this->prepareRequestUri();
  2183. }
  2184. return $this->requestUri;
  2185. }
  2186. public function getSchemeAndHttpHost()
  2187. {
  2188. return $this->getScheme().'://'.$this->getHttpHost();
  2189. }
  2190. public function getUri()
  2191. {
  2192. $qs = $this->getQueryString();
  2193. if (null !== $qs) {
  2194. $qs = '?'.$qs;
  2195. }
  2196. return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
  2197. }
  2198. public function getUriForPath($path)
  2199. {
  2200. return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
  2201. }
  2202. public function getQueryString()
  2203. {
  2204. $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
  2205. return '' === $qs ? null : $qs;
  2206. }
  2207. public function isSecure()
  2208. {
  2209. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) {
  2210. return in_array(strtolower($proto), array('https', 'on', '1'));
  2211. }
  2212. return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS');
  2213. }
  2214. public function getHost()
  2215. {
  2216. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) {
  2217. $elements = explode(',', $host);
  2218. $host = $elements[count($elements) - 1];
  2219. } elseif (!$host = $this->headers->get('HOST')) {
  2220. if (!$host = $this->server->get('SERVER_NAME')) {
  2221. $host = $this->server->get('SERVER_ADDR', '');
  2222. }
  2223. }
  2224. $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
  2225. if ($host && !preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) {
  2226. throw new \UnexpectedValueException('Invalid Host');
  2227. }
  2228. return $host;
  2229. }
  2230. public function setMethod($method)
  2231. {
  2232. $this->method = null;
  2233. $this->server->set('REQUEST_METHOD', $method);
  2234. }
  2235. public function getMethod()
  2236. {
  2237. if (null === $this->method) {
  2238. $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
  2239. if ('POST' === $this->method) {
  2240. $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', $this->query->get('_method', 'POST'))));
  2241. }
  2242. }
  2243. return $this->method;
  2244. }
  2245. public function getMimeType($format)
  2246. {
  2247. if (null === static::$formats) {
  2248. static::initializeFormats();
  2249. }
  2250. return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
  2251. }
  2252. public function getFormat($mimeType)
  2253. {
  2254. if (false !== $pos = strpos($mimeType, ';')) {
  2255. $mimeType = substr($mimeType, 0, $pos);
  2256. }
  2257. if (null === static::$formats) {
  2258. static::initializeFormats();
  2259. }
  2260. foreach (static::$formats as $format => $mimeTypes) {
  2261. if (in_array($mimeType, (array) $mimeTypes)) {
  2262. return $format;
  2263. }
  2264. }
  2265. return null;
  2266. }
  2267. public function setFormat($format, $mimeTypes)
  2268. {
  2269. if (null === static::$formats) {
  2270. static::initializeFormats();
  2271. }
  2272. static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
  2273. }
  2274. public function getRequestFormat($default = 'html')
  2275. {
  2276. if (null === $this->format) {
  2277. $this->format = $this->get('_format', $default);
  2278. }
  2279. return $this->format;
  2280. }
  2281. public function setRequestFormat($format)
  2282. {
  2283. $this->format = $format;
  2284. }
  2285. public function getContentType()
  2286. {
  2287. return $this->getFormat($this->headers->get('CONTENT_TYPE'));
  2288. }
  2289. public function setDefaultLocale($locale)
  2290. {
  2291. $this->defaultLocale = $locale;
  2292. if (null === $this->locale) {
  2293. $this->setPhpDefaultLocale($locale);
  2294. }
  2295. }
  2296. public function setLocale($locale)
  2297. {
  2298. $this->setPhpDefaultLocale($this->locale = $locale);
  2299. }
  2300. public function getLocale()
  2301. {
  2302. return null === $this->locale ? $this->defaultLocale : $this->locale;
  2303. }
  2304. public function isMethod($method)
  2305. {
  2306. return $this->getMethod() === strtoupper($method);
  2307. }
  2308. public function isMethodSafe()
  2309. {
  2310. return in_array($this->getMethod(), array('GET', 'HEAD'));
  2311. }
  2312. public function getContent($asResource = false)
  2313. {
  2314. if (false === $this->content || (true === $asResource && null !== $this->content)) {
  2315. throw new \LogicException('getContent() can only be called once when using the resource return type.');
  2316. }
  2317. if (true === $asResource) {
  2318. $this->content = false;
  2319. return fopen('php://input', 'rb');
  2320. }
  2321. if (null === $this->content) {
  2322. $this->content = file_get_contents('php://input');
  2323. }
  2324. return $this->content;
  2325. }
  2326. public function getETags()
  2327. {
  2328. return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
  2329. }
  2330. public function isNoCache()
  2331. {
  2332. return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
  2333. }
  2334. public function getPreferredLanguage(array $locales = null)
  2335. {
  2336. $preferredLanguages = $this->getLanguages();
  2337. if (empty($locales)) {
  2338. return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
  2339. }
  2340. if (!$preferredLanguages) {
  2341. return $locales[0];
  2342. }
  2343. $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales));
  2344. return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
  2345. }
  2346. public function getLanguages()
  2347. {
  2348. if (null !== $this->languages) {
  2349. return $this->languages;
  2350. }
  2351. $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language'));
  2352. $this->languages = array();
  2353. foreach ($languages as $lang => $q) {
  2354. if (strstr($lang, '-')) {
  2355. $codes = explode('-', $lang);
  2356. if ($codes[0] == 'i') {
  2357. if (count($codes) > 1) {
  2358. $lang = $codes[1];
  2359. }
  2360. } else {
  2361. for ($i = 0, $max = count($codes); $i < $max; $i++) {
  2362. if ($i == 0) {
  2363. $lang = strtolower($codes[0]);
  2364. } else {
  2365. $lang .= '_'.strtoupper($codes[$i]);
  2366. }
  2367. }
  2368. }
  2369. }
  2370. $this->languages[] = $lang;
  2371. }
  2372. return $this->languages;
  2373. }
  2374. public function getCharsets()
  2375. {
  2376. if (null !== $this->charsets) {
  2377. return $this->charsets;
  2378. }
  2379. return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset')));
  2380. }
  2381. public function getAcceptableContentTypes()
  2382. {
  2383. if (null !== $this->acceptableContentTypes) {
  2384. return $this->acceptableContentTypes;
  2385. }
  2386. return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept')));
  2387. }
  2388. public function isXmlHttpRequest()
  2389. {
  2390. return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
  2391. }
  2392. public function splitHttpAcceptHeader($header)
  2393. {
  2394. if (!$header) {
  2395. return array();
  2396. }
  2397. $values = array();
  2398. $groups = array();
  2399. foreach (array_filter(explode(',', $header)) as $value) {
  2400. if (preg_match('/;\s*(q=.*$)/', $value, $match)) {
  2401. $q = substr(trim($match[1]), 2);
  2402. $value = trim(substr($value, 0, -strlen($match[0])));
  2403. } else {
  2404. $q = 1;
  2405. }
  2406. $groups[$q][] = $value;
  2407. }
  2408. krsort($groups);
  2409. foreach ($groups as $q => $items) {
  2410. $q = (float) $q;
  2411. if (0 < $q) {
  2412. foreach ($items as $value) {
  2413. $values[trim($value)] = $q;
  2414. }
  2415. }
  2416. }
  2417. return $values;
  2418. }
  2419. protected function prepareRequestUri()
  2420. {
  2421. $requestUri = '';
  2422. if ($this->headers->has('X_ORIGINAL_URL') && false !== stripos(PHP_OS, 'WIN')) {
  2423. $requestUri = $this->headers->get('X_ORIGINAL_URL');
  2424. } elseif ($this->headers->has('X_REWRITE_URL') && false !== stripos(PHP_OS, 'WIN')) {
  2425. $requestUri = $this->headers->get('X_REWRITE_URL');
  2426. } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
  2427. $requestUri = $this->server->get('UNENCODED_URL');
  2428. } elseif ($this->server->has('REQUEST_URI')) {
  2429. $requestUri = $this->server->get('REQUEST_URI');
  2430. $schemeAndHttpHost = $this->getSchemeAndHttpHost();
  2431. if (strpos($requestUri, $schemeAndHttpHost) === 0) {
  2432. $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
  2433. }
  2434. } elseif ($this->server->has('ORIG_PATH_INFO')) {
  2435. $requestUri = $this->server->get('ORIG_PATH_INFO');
  2436. if ('' != $this->server->get('QUERY_STRING')) {
  2437. $requestUri .= '?'.$this->server->get('QUERY_STRING');
  2438. }
  2439. }
  2440. return $requestUri;
  2441. }
  2442. protected function prepareBaseUrl()
  2443. {
  2444. $filename = basename($this->server->get('SCRIPT_FILENAME'));
  2445. if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
  2446. $baseUrl = $this->server->get('SCRIPT_NAME');
  2447. } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
  2448. $baseUrl = $this->server->get('PHP_SELF');
  2449. } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
  2450. $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); } else {
  2451. $path = $this->server->get('PHP_SELF', '');
  2452. $file = $this->server->get('SCRIPT_FILENAME', '');
  2453. $segs = explode('/', trim($file, '/'));
  2454. $segs = array_reverse($segs);
  2455. $index = 0;
  2456. $last = count($segs);
  2457. $baseUrl = '';
  2458. do {
  2459. $seg = $segs[$index];
  2460. $baseUrl = '/'.$seg.$baseUrl;
  2461. ++$index;
  2462. } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
  2463. }
  2464. $requestUri = $this->getRequestUri();
  2465. if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
  2466. return $prefix;
  2467. }
  2468. if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl))) {
  2469. return rtrim($prefix, '/');
  2470. }
  2471. $truncatedRequestUri = $requestUri;
  2472. if (($pos = strpos($requestUri, '?')) !== false) {
  2473. $truncatedRequestUri = substr($requestUri, 0, $pos);
  2474. }
  2475. $basename = basename($baseUrl);
  2476. if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
  2477. return '';
  2478. }
  2479. if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
  2480. $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
  2481. }
  2482. return rtrim($baseUrl, '/');
  2483. }
  2484. protected function prepareBasePath()
  2485. {
  2486. $filename = basename($this->server->get('SCRIPT_FILENAME'));
  2487. $baseUrl = $this->getBaseUrl();
  2488. if (empty($baseUrl)) {
  2489. return '';
  2490. }
  2491. if (basename($baseUrl) === $filename) {
  2492. $basePath = dirname($baseUrl);
  2493. } else {
  2494. $basePath = $baseUrl;
  2495. }
  2496. if ('\\' === DIRECTORY_SEPARATOR) {
  2497. $basePath = str_replace('\\', '/', $basePath);
  2498. }
  2499. return rtrim($basePath, '/');
  2500. }
  2501. protected function preparePathInfo()
  2502. {
  2503. $baseUrl = $this->getBaseUrl();
  2504. if (null === ($requestUri = $this->getRequestUri())) {
  2505. return '/';
  2506. }
  2507. $pathInfo = '/';
  2508. if ($pos = strpos($requestUri, '?')) {
  2509. $requestUri = substr($requestUri, 0, $pos);
  2510. }
  2511. if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) {
  2512. return '/';
  2513. } elseif (null === $baseUrl) {
  2514. return $requestUri;
  2515. }
  2516. return (string) $pathInfo;
  2517. }
  2518. protected static function initializeFormats()
  2519. {
  2520. static::$formats = array(
  2521. 'html' => array('text/html', 'application/xhtml+xml'),
  2522. 'txt' => array('text/plain'),
  2523. 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
  2524. 'css' => array('text/css'),
  2525. 'json' => array('application/json', 'application/x-json'),
  2526. 'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
  2527. 'rdf' => array('application/rdf+xml'),
  2528. 'atom' => array('application/atom+xml'),
  2529. 'rss' => array('application/rss+xml'),
  2530. );
  2531. }
  2532. private function setPhpDefaultLocale($locale)
  2533. {
  2534. try {
  2535. if (class_exists('Locale', false)) {
  2536. \Locale::setDefault($locale);
  2537. }
  2538. } catch (\Exception $e) {
  2539. }
  2540. }
  2541. private function getUrlencodedPrefix($string, $prefix)
  2542. {
  2543. if (0 !== strpos(rawurldecode($string), $prefix)) {
  2544. return false;
  2545. }
  2546. $len = strlen($prefix);
  2547. if (preg_match("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) {
  2548. return $match[0];
  2549. }
  2550. return false;
  2551. }
  2552. }
  2553. }
  2554. namespace Symfony\Component\HttpFoundation
  2555. {
  2556. class Response
  2557. {
  2558. public $headers;
  2559. protected $content;
  2560. protected $version;
  2561. protected $statusCode;
  2562. protected $statusText;
  2563. protected $charset;
  2564. public static $statusTexts = array(
  2565. 100 => 'Continue',
  2566. 101 => 'Switching Protocols',
  2567. 102 => 'Processing', 200 => 'OK',
  2568. 201 => 'Created',
  2569. 202 => 'Accepted',
  2570. 203 => 'Non-Authoritative Information',
  2571. 204 => 'No Content',
  2572. 205 => 'Reset Content',
  2573. 206 => 'Partial Content',
  2574. 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used', 300 => 'Multiple Choices',
  2575. 301 => 'Moved Permanently',
  2576. 302 => 'Found',
  2577. 303 => 'See Other',
  2578. 304 => 'Not Modified',
  2579. 305 => 'Use Proxy',
  2580. 306 => 'Reserved',
  2581. 307 => 'Temporary Redirect',
  2582. 308 => 'Permanent Redirect', 400 => 'Bad Request',
  2583. 401 => 'Unauthorized',
  2584. 402 => 'Payment Required',
  2585. 403 => 'Forbidden',
  2586. 404 => 'Not Found',
  2587. 405 => 'Method Not Allowed',
  2588. 406 => 'Not Acceptable',
  2589. 407 => 'Proxy Authentication Required',
  2590. 408 => 'Request Timeout',
  2591. 409 => 'Conflict',
  2592. 410 => 'Gone',
  2593. 411 => 'Length Required',
  2594. 412 => 'Precondition Failed',
  2595. 413 => 'Request Entity Too Large',
  2596. 414 => 'Request-URI Too Long',
  2597. 415 => 'Unsupported Media Type',
  2598. 416 => 'Requested Range Not Satisfiable',
  2599. 417 => 'Expectation Failed',
  2600. 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Reserved for WebDAV advanced collections expired proposal', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 500 => 'Internal Server Error',
  2601. 501 => 'Not Implemented',
  2602. 502 => 'Bad Gateway',
  2603. 503 => 'Service Unavailable',
  2604. 504 => 'Gateway Timeout',
  2605. 505 => 'HTTP Version Not Supported',
  2606. 506 => 'Variant Also Negotiates (Experimental)', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required', );
  2607. public function __construct($content = '', $status = 200, $headers = array())
  2608. {
  2609. $this->headers = new ResponseHeaderBag($headers);
  2610. $this->setContent($content);
  2611. $this->setStatusCode($status);
  2612. $this->setProtocolVersion('1.0');
  2613. if (!$this->headers->has('Date')) {
  2614. $this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
  2615. }
  2616. }
  2617. public static function create($content = '', $status = 200, $headers = array())
  2618. {
  2619. return new static($content, $status, $headers);
  2620. }
  2621. public function __toString()
  2622. {
  2623. return
  2624. sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
  2625. $this->headers."\r\n".
  2626. $this->getContent();
  2627. }
  2628. public function __clone()
  2629. {
  2630. $this->headers = clone $this->headers;
  2631. }
  2632. public function prepare(Request $request)
  2633. {
  2634. $headers = $this->headers;
  2635. if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) {
  2636. $this->setContent(null);
  2637. }
  2638. if (!$headers->has('Content-Type')) {
  2639. $format = $request->getRequestFormat();
  2640. if (null !== $format && $mimeType = $request->getMimeType($format)) {
  2641. $headers->set('Content-Type', $mimeType);
  2642. }
  2643. }
  2644. $charset = $this->charset ?: 'UTF-8';
  2645. if (!$headers->has('Content-Type')) {
  2646. $headers->set('Content-Type', 'text/html; charset='.$charset);
  2647. } elseif (0 === strpos($headers->get('Content-Type'), 'text/') && false === strpos($headers->get('Content-Type'), 'charset')) {
  2648. $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
  2649. }
  2650. if ($headers->has('Transfer-Encoding')) {
  2651. $headers->remove('Content-Length');
  2652. }
  2653. if ('HEAD' === $request->getMethod()) {
  2654. $length = $headers->get('Content-Length');
  2655. $this->setContent(null);
  2656. if ($length) {
  2657. $headers->set('Content-Length', $length);
  2658. }
  2659. }
  2660. if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
  2661. $this->setProtocolVersion('1.1');
  2662. }
  2663. if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) {
  2664. $this->headers->set('pragma', 'no-cache');
  2665. $this->headers->set('expires', -1);
  2666. }
  2667. return $this;
  2668. }
  2669. public function sendHeaders()
  2670. {
  2671. if (headers_sent()) {
  2672. return $this;
  2673. }
  2674. header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
  2675. foreach ($this->headers->all() as $name => $values) {
  2676. foreach ($values as $value) {
  2677. header($name.': '.$value, false);
  2678. }
  2679. }
  2680. foreach ($this->headers->getCookies() as $cookie) {
  2681. setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
  2682. }
  2683. return $this;
  2684. }
  2685. public function sendContent()
  2686. {
  2687. echo $this->content;
  2688. return $this;
  2689. }
  2690. public function send()
  2691. {
  2692. $this->sendHeaders();
  2693. $this->sendContent();
  2694. if (function_exists('fastcgi_finish_request')) {
  2695. fastcgi_finish_request();
  2696. } elseif ('cli' !== PHP_SAPI) {
  2697. $previous = null;
  2698. $obStatus = ob_get_status(1);
  2699. while (($level = ob_get_level()) > 0 && $level !== $previous) {
  2700. $previous = $level;
  2701. if ($obStatus[$level - 1] && isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) {
  2702. ob_end_flush();
  2703. }
  2704. }
  2705. flush();
  2706. }
  2707. return $this;
  2708. }
  2709. public function setContent($content)
  2710. {
  2711. if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
  2712. throw new \UnexpectedValueException('The Response content must be a string or object implementing __toString(), "'.gettype($content).'" given.');
  2713. }
  2714. $this->content = (string) $content;
  2715. return $this;
  2716. }
  2717. public function getContent()
  2718. {
  2719. return $this->content;
  2720. }
  2721. public function setProtocolVersion($version)
  2722. {
  2723. $this->version = $version;
  2724. return $this;
  2725. }
  2726. public function getProtocolVersion()
  2727. {
  2728. return $this->version;
  2729. }
  2730. public function setStatusCode($code, $text = null)
  2731. {
  2732. $this->statusCode = $code = (int) $code;
  2733. if ($this->isInvalid()) {
  2734. throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
  2735. }
  2736. if (null === $text) {
  2737. $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : '';
  2738. return $this;
  2739. }
  2740. if (false === $text) {
  2741. $this->statusText = '';
  2742. return $this;
  2743. }
  2744. $this->statusText = $text;
  2745. return $this;
  2746. }
  2747. public function getStatusCode()
  2748. {
  2749. return $this->statusCode;
  2750. }
  2751. public function setCharset($charset)
  2752. {
  2753. $this->charset = $charset;
  2754. return $this;
  2755. }
  2756. public function getCharset()
  2757. {
  2758. return $this->charset;
  2759. }
  2760. public function isCacheable()
  2761. {
  2762. if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
  2763. return false;
  2764. }
  2765. if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
  2766. return false;
  2767. }
  2768. return $this->isValidateable() || $this->isFresh();
  2769. }
  2770. public function isFresh()
  2771. {
  2772. return $this->getTtl() > 0;
  2773. }
  2774. public function isValidateable()
  2775. {
  2776. return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
  2777. }
  2778. public function setPrivate()
  2779. {
  2780. $this->headers->removeCacheControlDirective('public');
  2781. $this->headers->addCacheControlDirective('private');
  2782. return $this;
  2783. }
  2784. public function setPublic()
  2785. {
  2786. $this->headers->addCacheControlDirective('public');
  2787. $this->headers->removeCacheControlDirective('private');
  2788. return $this;
  2789. }
  2790. public function mustRevalidate()
  2791. {
  2792. return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('proxy-revalidate');
  2793. }
  2794. public function getDate()
  2795. {
  2796. return $this->headers->getDate('Date', new \DateTime());
  2797. }
  2798. public function setDate(\DateTime $date)
  2799. {
  2800. $date->setTimezone(new \DateTimeZone('UTC'));
  2801. $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
  2802. return $this;
  2803. }
  2804. public function getAge()
  2805. {
  2806. if (null !== $age = $this->headers->get('Age')) {
  2807. return (int) $age;
  2808. }
  2809. return max(time() - $this->getDate()->format('U'), 0);
  2810. }
  2811. public function expire()
  2812. {
  2813. if ($this->isFresh()) {
  2814. $this->headers->set('Age', $this->getMaxAge());
  2815. }
  2816. return $this;
  2817. }
  2818. public function getExpires()
  2819. {
  2820. try {
  2821. return $this->headers->getDate('Expires');
  2822. } catch (\RuntimeException $e) {
  2823. return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
  2824. }
  2825. }
  2826. public function setExpires(\DateTime $date = null)
  2827. {
  2828. if (null === $date) {
  2829. $this->headers->remove('Expires');
  2830. } else {
  2831. $date = clone $date;
  2832. $date->setTimezone(new \DateTimeZone('UTC'));
  2833. $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
  2834. }
  2835. return $this;
  2836. }
  2837. public function getMaxAge()
  2838. {
  2839. if ($this->headers->hasCacheControlDirective('s-maxage')) {
  2840. return (int) $this->headers->getCacheControlDirective('s-maxage');
  2841. }
  2842. if ($this->headers->hasCacheControlDirective('max-age')) {
  2843. return (int) $this->headers->getCacheControlDirective('max-age');
  2844. }
  2845. if (null !== $this->getExpires()) {
  2846. return $this->getExpires()->format('U') - $this->getDate()->format('U');
  2847. }
  2848. return null;
  2849. }
  2850. public function setMaxAge($value)
  2851. {
  2852. $this->headers->addCacheControlDirective('max-age', $value);
  2853. return $this;
  2854. }
  2855. public function setSharedMaxAge($value)
  2856. {
  2857. $this->setPublic();
  2858. $this->headers->addCacheControlDirective('s-maxage', $value);
  2859. return $this;
  2860. }
  2861. public function getTtl()
  2862. {
  2863. if (null !== $maxAge = $this->getMaxAge()) {
  2864. return $maxAge - $this->getAge();
  2865. }
  2866. return null;
  2867. }
  2868. public function setTtl($seconds)
  2869. {
  2870. $this->setSharedMaxAge($this->getAge() + $seconds);
  2871. return $this;
  2872. }
  2873. public function setClientTtl($seconds)
  2874. {
  2875. $this->setMaxAge($this->getAge() + $seconds);
  2876. return $this;
  2877. }
  2878. public function getLastModified()
  2879. {
  2880. return $this->headers->getDate('Last-Modified');
  2881. }
  2882. public function setLastModified(\DateTime $date = null)
  2883. {
  2884. if (null === $date) {
  2885. $this->headers->remove('Last-Modified');
  2886. } else {
  2887. $date = clone $date;
  2888. $date->setTimezone(new \DateTimeZone('UTC'));
  2889. $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
  2890. }
  2891. return $this;
  2892. }
  2893. public function getEtag()
  2894. {
  2895. return $this->headers->get('ETag');
  2896. }
  2897. public function setEtag($etag = null, $weak = false)
  2898. {
  2899. if (null === $etag) {
  2900. $this->headers->remove('Etag');
  2901. } else {
  2902. if (0 !== strpos($etag, '"')) {
  2903. $etag = '"'.$etag.'"';
  2904. }
  2905. $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
  2906. }
  2907. return $this;
  2908. }
  2909. public function setCache(array $options)
  2910. {
  2911. if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
  2912. throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
  2913. }
  2914. if (isset($options['etag'])) {
  2915. $this->setEtag($options['etag']);
  2916. }
  2917. if (isset($options['last_modified'])) {
  2918. $this->setLastModified($options['last_modified']);
  2919. }
  2920. if (isset($options['max_age'])) {
  2921. $this->setMaxAge($options['max_age']);
  2922. }
  2923. if (isset($options['s_maxage'])) {
  2924. $this->setSharedMaxAge($options['s_maxage']);
  2925. }
  2926. if (isset($options['public'])) {
  2927. if ($options['public']) {
  2928. $this->setPublic();
  2929. } else {
  2930. $this->setPrivate();
  2931. }
  2932. }
  2933. if (isset($options['private'])) {
  2934. if ($options['private']) {
  2935. $this->setPrivate();
  2936. } else {
  2937. $this->setPublic();
  2938. }
  2939. }
  2940. return $this;
  2941. }
  2942. public function setNotModified()
  2943. {
  2944. $this->setStatusCode(304);
  2945. $this->setContent(null);
  2946. foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
  2947. $this->headers->remove($header);
  2948. }
  2949. return $this;
  2950. }
  2951. public function hasVary()
  2952. {
  2953. return null !== $this->headers->get('Vary');
  2954. }
  2955. public function getVary()
  2956. {
  2957. if (!$vary = $this->headers->get('Vary')) {
  2958. return array();
  2959. }
  2960. return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary);
  2961. }
  2962. public function setVary($headers, $replace = true)
  2963. {
  2964. $this->headers->set('Vary', $headers, $replace);
  2965. return $this;
  2966. }
  2967. public function isNotModified(Request $request)
  2968. {
  2969. if (!$request->isMethodSafe()) {
  2970. return false;
  2971. }
  2972. $lastModified = $request->headers->get('If-Modified-Since');
  2973. $notModified = false;
  2974. if ($etags = $request->getEtags()) {
  2975. $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified);
  2976. } elseif ($lastModified) {
  2977. $notModified = $lastModified == $this->headers->get('Last-Modified');
  2978. }
  2979. if ($notModified) {
  2980. $this->setNotModified();
  2981. }
  2982. return $notModified;
  2983. }
  2984. public function isInvalid()
  2985. {
  2986. return $this->statusCode < 100 || $this->statusCode >= 600;
  2987. }
  2988. public function isInformational()
  2989. {
  2990. return $this->statusCode >= 100 && $this->statusCode < 200;
  2991. }
  2992. public function isSuccessful()
  2993. {
  2994. return $this->statusCode >= 200 && $this->statusCode < 300;
  2995. }
  2996. public function isRedirection()
  2997. {
  2998. return $this->statusCode >= 300 && $this->statusCode < 400;
  2999. }
  3000. public function isClientError()
  3001. {
  3002. return $this->statusCode >= 400 && $this->statusCode < 500;
  3003. }
  3004. public function isServerError()
  3005. {
  3006. return $this->statusCode >= 500 && $this->statusCode < 600;
  3007. }
  3008. public function isOk()
  3009. {
  3010. return 200 === $this->statusCode;
  3011. }
  3012. public function isForbidden()
  3013. {
  3014. return 403 === $this->statusCode;
  3015. }
  3016. public function isNotFound()
  3017. {
  3018. return 404 === $this->statusCode;
  3019. }
  3020. public function isRedirect($location = null)
  3021. {
  3022. return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
  3023. }
  3024. public function isEmpty()
  3025. {
  3026. return in_array($this->statusCode, array(201, 204, 304));
  3027. }
  3028. }
  3029. }
  3030. namespace Symfony\Component\HttpFoundation
  3031. {
  3032. class ResponseHeaderBag extends HeaderBag
  3033. {
  3034. const COOKIES_FLAT = 'flat';
  3035. const COOKIES_ARRAY = 'array';
  3036. const DISPOSITION_ATTACHMENT = 'attachment';
  3037. const DISPOSITION_INLINE = 'inline';
  3038. protected $computedCacheControl = array();
  3039. protected $cookies = array();
  3040. public function __construct(array $headers = array())
  3041. {
  3042. parent::__construct($headers);
  3043. if (!isset($this->headers['cache-control'])) {
  3044. $this->set('cache-control', '');
  3045. }
  3046. }
  3047. public function __toString()
  3048. {
  3049. $cookies = '';
  3050. foreach ($this->getCookies() as $cookie) {
  3051. $cookies .= 'Set-Cookie: '.$cookie."\r\n";
  3052. }
  3053. return parent::__toString().$cookies;
  3054. }
  3055. public function replace(array $headers = array())
  3056. {
  3057. parent::replace($headers);
  3058. if (!isset($this->headers['cache-control'])) {
  3059. $this->set('cache-control', '');
  3060. }
  3061. }
  3062. public function set($key, $values, $replace = true)
  3063. {
  3064. parent::set($key, $values, $replace);
  3065. if (in_array(strtr(strtolower($key), '_', '-'), array('cache-control', 'etag', 'last-modified', 'expires'))) {
  3066. $computed = $this->computeCacheControlValue();
  3067. $this->headers['cache-control'] = array($computed);
  3068. $this->computedCacheControl = $this->parseCacheControl($computed);
  3069. }
  3070. }
  3071. public function remove($key)
  3072. {
  3073. parent::remove($key);
  3074. if ('cache-control' === strtr(strtolower($key), '_', '-')) {
  3075. $this->computedCacheControl = array();
  3076. }
  3077. }
  3078. public function hasCacheControlDirective($key)
  3079. {
  3080. return array_key_exists($key, $this->computedCacheControl);
  3081. }
  3082. public function getCacheControlDirective($key)
  3083. {
  3084. return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
  3085. }
  3086. public function setCookie(Cookie $cookie)
  3087. {
  3088. $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
  3089. }
  3090. public function removeCookie($name, $path = '/', $domain = null)
  3091. {
  3092. if (null === $path) {
  3093. $path = '/';
  3094. }
  3095. unset($this->cookies[$domain][$path][$name]);
  3096. if (empty($this->cookies[$domain][$path])) {
  3097. unset($this->cookies[$domain][$path]);
  3098. if (empty($this->cookies[$domain])) {
  3099. unset($this->cookies[$domain]);
  3100. }
  3101. }
  3102. }
  3103. public function getCookies($format = self::COOKIES_FLAT)
  3104. {
  3105. if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
  3106. throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
  3107. }
  3108. if (self::COOKIES_ARRAY === $format) {
  3109. return $this->cookies;
  3110. }
  3111. $flattenedCookies = array();
  3112. foreach ($this->cookies as $path) {
  3113. foreach ($path as $cookies) {
  3114. foreach ($cookies as $cookie) {
  3115. $flattenedCookies[] = $cookie;
  3116. }
  3117. }
  3118. }
  3119. return $flattenedCookies;
  3120. }
  3121. public function clearCookie($name, $path = '/', $domain = null)
  3122. {
  3123. $this->setCookie(new Cookie($name, null, 1, $path, $domain));
  3124. }
  3125. public function makeDisposition($disposition, $filename, $filenameFallback = '')
  3126. {
  3127. if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
  3128. throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
  3129. }
  3130. if ('' == $filenameFallback) {
  3131. $filenameFallback = $filename;
  3132. }
  3133. if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
  3134. throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
  3135. }
  3136. if (false !== strpos($filenameFallback, '%')) {
  3137. throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
  3138. }
  3139. if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
  3140. throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
  3141. }
  3142. $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
  3143. if ($filename !== $filenameFallback) {
  3144. $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
  3145. }
  3146. return $output;
  3147. }
  3148. protected function computeCacheControlValue()
  3149. {
  3150. if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
  3151. return 'no-cache';
  3152. }
  3153. if (!$this->cacheControl) {
  3154. return 'private, must-revalidate';
  3155. }
  3156. $header = $this->getCacheControlHeader();
  3157. if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
  3158. return $header;
  3159. }
  3160. if (!isset($this->cacheControl['s-maxage'])) {
  3161. return $header.', private';
  3162. }
  3163. return $header;
  3164. }
  3165. }
  3166. }
  3167. namespace Symfony\Component\Config
  3168. {
  3169. class FileLocator implements FileLocatorInterface
  3170. {
  3171. protected $paths;
  3172. public function __construct($paths = array())
  3173. {
  3174. $this->paths = (array) $paths;
  3175. }
  3176. public function locate($name, $currentPath = null, $first = true)
  3177. {
  3178. if ($this->isAbsolutePath($name)) {
  3179. if (!file_exists($name)) {
  3180. throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name));
  3181. }
  3182. return $name;
  3183. }
  3184. $filepaths = array();
  3185. if (null !== $currentPath && file_exists($file = $currentPath.DIRECTORY_SEPARATOR.$name)) {
  3186. if (true === $first) {
  3187. return $file;
  3188. }
  3189. $filepaths[] = $file;
  3190. }
  3191. foreach ($this->paths as $path) {
  3192. if (file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) {
  3193. if (true === $first) {
  3194. return $file;
  3195. }
  3196. $filepaths[] = $file;
  3197. }
  3198. }
  3199. if (!$filepaths) {
  3200. throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s%s).', $name, null !== $currentPath ? $currentPath.', ' : '', implode(', ', $this->paths)));
  3201. }
  3202. return array_values(array_unique($filepaths));
  3203. }
  3204. private function isAbsolutePath($file)
  3205. {
  3206. if ($file[0] == '/' || $file[0] == '\\'
  3207. || (strlen($file) > 3 && ctype_alpha($file[0])
  3208. && $file[1] == ':'
  3209. && ($file[2] == '\\' || $file[2] == '/')
  3210. )
  3211. || null !== parse_url($file, PHP_URL_SCHEME)
  3212. ) {
  3213. return true;
  3214. }
  3215. return false;
  3216. }
  3217. }
  3218. }
  3219. namespace Symfony\Component\EventDispatcher
  3220. {
  3221. class Event
  3222. {
  3223. private $propagationStopped = false;
  3224. private $dispatcher;
  3225. private $name;
  3226. public function isPropagationStopped()
  3227. {
  3228. return $this->propagationStopped;
  3229. }
  3230. public function stopPropagation()
  3231. {
  3232. $this->propagationStopped = true;
  3233. }
  3234. public function setDispatcher(EventDispatcher $dispatcher)
  3235. {
  3236. $this->dispatcher = $dispatcher;
  3237. }
  3238. public function getDispatcher()
  3239. {
  3240. return $this->dispatcher;
  3241. }
  3242. public function getName()
  3243. {
  3244. return $this->name;
  3245. }
  3246. public function setName($name)
  3247. {
  3248. $this->name = $name;
  3249. }
  3250. }
  3251. }
  3252. namespace Symfony\Component\EventDispatcher
  3253. {
  3254. interface EventDispatcherInterface
  3255. {
  3256. public function dispatch($eventName, Event $event = null);
  3257. public function addListener($eventName, $listener, $priority = 0);
  3258. public function addSubscriber(EventSubscriberInterface $subscriber);
  3259. public function removeListener($eventName, $listener);
  3260. public function removeSubscriber(EventSubscriberInterface $subscriber);
  3261. public function getListeners($eventName = null);
  3262. public function hasListeners($eventName = null);
  3263. }
  3264. }
  3265. namespace Symfony\Component\EventDispatcher
  3266. {
  3267. class EventDispatcher implements EventDispatcherInterface
  3268. {
  3269. private $listeners = array();
  3270. private $sorted = array();
  3271. public function dispatch($eventName, Event $event = null)
  3272. {
  3273. if (null === $event) {
  3274. $event = new Event();
  3275. }
  3276. $event->setDispatcher($this);
  3277. $event->setName($eventName);
  3278. if (!isset($this->listeners[$eventName])) {
  3279. return $event;
  3280. }
  3281. $this->doDispatch($this->getListeners($eventName), $eventName, $event);
  3282. return $event;
  3283. }
  3284. public function getListeners($eventName = null)
  3285. {
  3286. if (null !== $eventName) {
  3287. if (!isset($this->sorted[$eventName])) {
  3288. $this->sortListeners($eventName);
  3289. }
  3290. return $this->sorted[$eventName];
  3291. }
  3292. foreach (array_keys($this->listeners) as $eventName) {
  3293. if (!isset($this->sorted[$eventName])) {
  3294. $this->sortListeners($eventName);
  3295. }
  3296. }
  3297. return $this->sorted;
  3298. }
  3299. public function hasListeners($eventName = null)
  3300. {
  3301. return (Boolean) count($this->getListeners($eventName));
  3302. }
  3303. public function addListener($eventName, $listener, $priority = 0)
  3304. {
  3305. $this->listeners[$eventName][$priority][] = $listener;
  3306. unset($this->sorted[$eventName]);
  3307. }
  3308. public function removeListener($eventName, $listener)
  3309. {
  3310. if (!isset($this->listeners[$eventName])) {
  3311. return;
  3312. }
  3313. foreach ($this->listeners[$eventName] as $priority => $listeners) {
  3314. if (false !== ($key = array_search($listener, $listeners))) {
  3315. unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
  3316. }
  3317. }
  3318. }
  3319. public function addSubscriber(EventSubscriberInterface $subscriber)
  3320. {
  3321. foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
  3322. if (is_string($params)) {
  3323. $this->addListener($eventName, array($subscriber, $params));
  3324. } elseif (is_string($params[0])) {
  3325. $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
  3326. } else {
  3327. foreach ($params as $listener) {
  3328. $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
  3329. }
  3330. }
  3331. }
  3332. }
  3333. public function removeSubscriber(EventSubscriberInterface $subscriber)
  3334. {
  3335. foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
  3336. if (is_array($params) && is_array($params[0])) {
  3337. foreach ($params as $listener) {
  3338. $this->removeListener($eventName, array($subscriber, $listener[0]));
  3339. }
  3340. } else {
  3341. $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
  3342. }
  3343. }
  3344. }
  3345. protected function doDispatch($listeners, $eventName, Event $event)
  3346. {
  3347. foreach ($listeners as $listener) {
  3348. call_user_func($listener, $event);
  3349. if ($event->isPropagationStopped()) {
  3350. break;
  3351. }
  3352. }
  3353. }
  3354. private function sortListeners($eventName)
  3355. {
  3356. $this->sorted[$eventName] = array();
  3357. if (isset($this->listeners[$eventName])) {
  3358. krsort($this->listeners[$eventName]);
  3359. $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
  3360. }
  3361. }
  3362. }
  3363. }
  3364. namespace Symfony\Component\EventDispatcher
  3365. {
  3366. use Symfony\Component\DependencyInjection\ContainerInterface;
  3367. class ContainerAwareEventDispatcher extends EventDispatcher
  3368. {
  3369. private $container;
  3370. private $listenerIds = array();
  3371. private $listeners = array();
  3372. public function __construct(ContainerInterface $container)
  3373. {
  3374. $this->container = $container;
  3375. }
  3376. public function addListenerService($eventName, $callback, $priority = 0)
  3377. {
  3378. if (!is_array($callback) || 2 !== count($callback)) {
  3379. throw new \InvalidArgumentException('Expected an array("service", "method") argument');
  3380. }
  3381. $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
  3382. }
  3383. public function removeListener($eventName, $listener)
  3384. {
  3385. $this->lazyLoad($eventName);
  3386. if (isset($this->listeners[$eventName])) {
  3387. foreach ($this->listeners[$eventName] as $key => $l) {
  3388. foreach ($this->listenerIds[$eventName] as $i => $args) {
  3389. list($serviceId, $method, $priority) = $args;
  3390. if ($key === $serviceId.'.'.$method) {
  3391. if ($listener === array($l, $method)) {
  3392. unset($this->listeners[$eventName][$key]);
  3393. if (empty($this->listeners[$eventName])) {
  3394. unset($this->listeners[$eventName]);
  3395. }
  3396. unset($this->listenerIds[$eventName][$i]);
  3397. if (empty($this->listenerIds[$eventName])) {
  3398. unset($this->listenerIds[$eventName]);
  3399. }
  3400. }
  3401. }
  3402. }
  3403. }
  3404. }
  3405. parent::removeListener($eventName, $listener);
  3406. }
  3407. public function hasListeners($eventName = null)
  3408. {
  3409. if (null === $eventName) {
  3410. return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners);
  3411. }
  3412. if (isset($this->listenerIds[$eventName])) {
  3413. return true;
  3414. }
  3415. return parent::hasListeners($eventName);
  3416. }
  3417. public function getListeners($eventName = null)
  3418. {
  3419. if (null === $eventName) {
  3420. foreach (array_keys($this->listenerIds) as $serviceEventName) {
  3421. $this->lazyLoad($serviceEventName);
  3422. }
  3423. } else {
  3424. $this->lazyLoad($eventName);
  3425. }
  3426. return parent::getListeners($eventName);
  3427. }
  3428. public function addSubscriberService($serviceId, $class)
  3429. {
  3430. foreach ($class::getSubscribedEvents() as $eventName => $params) {
  3431. if (is_string($params)) {
  3432. $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
  3433. } elseif (is_string($params[0])) {
  3434. $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
  3435. } else {
  3436. foreach ($params as $listener) {
  3437. $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
  3438. }
  3439. }
  3440. }
  3441. }
  3442. public function dispatch($eventName, Event $event = null)
  3443. {
  3444. $this->lazyLoad($eventName);
  3445. return parent::dispatch($eventName, $event);
  3446. }
  3447. public function getContainer()
  3448. {
  3449. return $this->container;
  3450. }
  3451. protected function lazyLoad($eventName)
  3452. {
  3453. if (isset($this->listenerIds[$eventName])) {
  3454. foreach ($this->listenerIds[$eventName] as $args) {
  3455. list($serviceId, $method, $priority) = $args;
  3456. $listener = $this->container->get($serviceId);
  3457. $key = $serviceId.'.'.$method;
  3458. if (!isset($this->listeners[$eventName][$key])) {
  3459. $this->addListener($eventName, array($listener, $method), $priority);
  3460. } elseif ($listener !== $this->listeners[$eventName][$key]) {
  3461. parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
  3462. $this->addListener($eventName, array($listener, $method), $priority);
  3463. }
  3464. $this->listeners[$eventName][$key] = $listener;
  3465. }
  3466. }
  3467. }
  3468. }
  3469. }
  3470. namespace Symfony\Component\HttpKernel\EventListener
  3471. {
  3472. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  3473. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3474. use Symfony\Component\HttpKernel\KernelEvents;
  3475. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  3476. class ResponseListener implements EventSubscriberInterface
  3477. {
  3478. private $charset;
  3479. public function __construct($charset)
  3480. {
  3481. $this->charset = $charset;
  3482. }
  3483. public function onKernelResponse(FilterResponseEvent $event)
  3484. {
  3485. if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  3486. return;
  3487. }
  3488. $response = $event->getResponse();
  3489. if (null === $response->getCharset()) {
  3490. $response->setCharset($this->charset);
  3491. }
  3492. $response->prepare($event->getRequest());
  3493. }
  3494. public static function getSubscribedEvents()
  3495. {
  3496. return array(
  3497. KernelEvents::RESPONSE => 'onKernelResponse',
  3498. );
  3499. }
  3500. }
  3501. }
  3502. namespace Symfony\Component\HttpKernel\EventListener
  3503. {
  3504. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  3505. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  3506. use Symfony\Component\HttpKernel\KernelEvents;
  3507. use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
  3508. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  3509. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  3510. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  3511. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  3512. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  3513. use Symfony\Component\Routing\RequestContext;
  3514. use Symfony\Component\Routing\RequestContextAwareInterface;
  3515. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  3516. class RouterListener implements EventSubscriberInterface
  3517. {
  3518. private $matcher;
  3519. private $context;
  3520. private $logger;
  3521. public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null)
  3522. {
  3523. if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) {
  3524. throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.');
  3525. }
  3526. if (null === $context && !$matcher instanceof RequestContextAwareInterface) {
  3527. throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.');
  3528. }
  3529. $this->matcher = $matcher;
  3530. $this->context = $context ?: $matcher->getContext();
  3531. $this->logger = $logger;
  3532. }
  3533. public function onKernelRequest(GetResponseEvent $event)
  3534. {
  3535. $request = $event->getRequest();
  3536. $this->context->fromRequest($request);
  3537. if ($request->attributes->has('_controller')) {
  3538. return;
  3539. }
  3540. try {
  3541. if ($this->matcher instanceof RequestMatcherInterface) {
  3542. $parameters = $this->matcher->matchRequest($request);
  3543. } else {
  3544. $parameters = $this->matcher->match($request->getPathInfo());
  3545. }
  3546. if (null !== $this->logger) {
  3547. $this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->parametersToString($parameters)));
  3548. }
  3549. $request->attributes->add($parameters);
  3550. unset($parameters['_route']);
  3551. unset($parameters['_controller']);
  3552. $request->attributes->set('_route_params', $parameters);
  3553. } catch (ResourceNotFoundException $e) {
  3554. $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
  3555. throw new NotFoundHttpException($message, $e);
  3556. } catch (MethodNotAllowedException $e) {
  3557. $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), strtoupper(implode(', ', $e->getAllowedMethods())));
  3558. throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e);
  3559. }
  3560. }
  3561. private function parametersToString(array $parameters)
  3562. {
  3563. $pieces = array();
  3564. foreach ($parameters as $key => $val) {
  3565. $pieces[] = sprintf('"%s": "%s"', $key, (is_string($val) ? $val : json_encode($val)));
  3566. }
  3567. return implode(', ', $pieces);
  3568. }
  3569. public static function getSubscribedEvents()
  3570. {
  3571. return array(
  3572. KernelEvents::REQUEST => array(array('onKernelRequest', 32)),
  3573. );
  3574. }
  3575. }
  3576. }
  3577. namespace Symfony\Component\HttpKernel\Controller
  3578. {
  3579. use Symfony\Component\HttpFoundation\Request;
  3580. interface ControllerResolverInterface
  3581. {
  3582. public function getController(Request $request);
  3583. public function getArguments(Request $request, $controller);
  3584. }
  3585. }
  3586. namespace Symfony\Component\HttpKernel\Controller
  3587. {
  3588. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  3589. use Symfony\Component\HttpFoundation\Request;
  3590. class ControllerResolver implements ControllerResolverInterface
  3591. {
  3592. private $logger;
  3593. public function __construct(LoggerInterface $logger = null)
  3594. {
  3595. $this->logger = $logger;
  3596. }
  3597. public function getController(Request $request)
  3598. {
  3599. if (!$controller = $request->attributes->get('_controller')) {
  3600. if (null !== $this->logger) {
  3601. $this->logger->warn('Unable to look for the controller as the "_controller" parameter is missing');
  3602. }
  3603. return false;
  3604. }
  3605. if (is_array($controller) || (is_object($controller) && method_exists($controller, '__invoke'))) {
  3606. return $controller;
  3607. }
  3608. if (false === strpos($controller, ':')) {
  3609. if (method_exists($controller, '__invoke')) {
  3610. return new $controller;
  3611. } elseif (function_exists($controller)) {
  3612. return $controller;
  3613. }
  3614. }
  3615. list($controller, $method) = $this->createController($controller);
  3616. if (!method_exists($controller, $method)) {
  3617. throw new \InvalidArgumentException(sprintf('Method "%s::%s" does not exist.', get_class($controller), $method));
  3618. }
  3619. return array($controller, $method);
  3620. }
  3621. public function getArguments(Request $request, $controller)
  3622. {
  3623. if (is_array($controller)) {
  3624. $r = new \ReflectionMethod($controller[0], $controller[1]);
  3625. } elseif (is_object($controller) && !$controller instanceof \Closure) {
  3626. $r = new \ReflectionObject($controller);
  3627. $r = $r->getMethod('__invoke');
  3628. } else {
  3629. $r = new \ReflectionFunction($controller);
  3630. }
  3631. return $this->doGetArguments($request, $controller, $r->getParameters());
  3632. }
  3633. protected function doGetArguments(Request $request, $controller, array $parameters)
  3634. {
  3635. $attributes = $request->attributes->all();
  3636. $arguments = array();
  3637. foreach ($parameters as $param) {
  3638. if (array_key_exists($param->name, $attributes)) {
  3639. $arguments[] = $attributes[$param->name];
  3640. } elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
  3641. $arguments[] = $request;
  3642. } elseif ($param->isDefaultValueAvailable()) {
  3643. $arguments[] = $param->getDefaultValue();
  3644. } else {
  3645. if (is_array($controller)) {
  3646. $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
  3647. } elseif (is_object($controller)) {
  3648. $repr = get_class($controller);
  3649. } else {
  3650. $repr = $controller;
  3651. }
  3652. throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name));
  3653. }
  3654. }
  3655. return $arguments;
  3656. }
  3657. protected function createController($controller)
  3658. {
  3659. if (false === strpos($controller, '::')) {
  3660. throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller));
  3661. }
  3662. list($class, $method) = explode('::', $controller, 2);
  3663. if (!class_exists($class)) {
  3664. throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
  3665. }
  3666. return array(new $class(), $method);
  3667. }
  3668. }
  3669. }
  3670. namespace Symfony\Component\HttpKernel\Event
  3671. {
  3672. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3673. use Symfony\Component\HttpFoundation\Request;
  3674. use Symfony\Component\EventDispatcher\Event;
  3675. class KernelEvent extends Event
  3676. {
  3677. private $kernel;
  3678. private $request;
  3679. private $requestType;
  3680. public function __construct(HttpKernelInterface $kernel, Request $request, $requestType)
  3681. {
  3682. $this->kernel = $kernel;
  3683. $this->request = $request;
  3684. $this->requestType = $requestType;
  3685. }
  3686. public function getKernel()
  3687. {
  3688. return $this->kernel;
  3689. }
  3690. public function getRequest()
  3691. {
  3692. return $this->request;
  3693. }
  3694. public function getRequestType()
  3695. {
  3696. return $this->requestType;
  3697. }
  3698. }
  3699. }
  3700. namespace Symfony\Component\HttpKernel\Event
  3701. {
  3702. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3703. use Symfony\Component\HttpFoundation\Request;
  3704. class FilterControllerEvent extends KernelEvent
  3705. {
  3706. private $controller;
  3707. public function __construct(HttpKernelInterface $kernel, $controller, Request $request, $requestType)
  3708. {
  3709. parent::__construct($kernel, $request, $requestType);
  3710. $this->setController($controller);
  3711. }
  3712. public function getController()
  3713. {
  3714. return $this->controller;
  3715. }
  3716. public function setController($controller)
  3717. {
  3718. if (!is_callable($controller)) {
  3719. throw new \LogicException(sprintf('The controller must be a callable (%s given).', $this->varToString($controller)));
  3720. }
  3721. $this->controller = $controller;
  3722. }
  3723. private function varToString($var)
  3724. {
  3725. if (is_object($var)) {
  3726. return sprintf('Object(%s)', get_class($var));
  3727. }
  3728. if (is_array($var)) {
  3729. $a = array();
  3730. foreach ($var as $k => $v) {
  3731. $a[] = sprintf('%s => %s', $k, $this->varToString($v));
  3732. }
  3733. return sprintf("Array(%s)", implode(', ', $a));
  3734. }
  3735. if (is_resource($var)) {
  3736. return sprintf('Resource(%s)', get_resource_type($var));
  3737. }
  3738. if (null === $var) {
  3739. return 'null';
  3740. }
  3741. if (false === $var) {
  3742. return 'false';
  3743. }
  3744. if (true === $var) {
  3745. return 'true';
  3746. }
  3747. return (string) $var;
  3748. }
  3749. }
  3750. }
  3751. namespace Symfony\Component\HttpKernel\Event
  3752. {
  3753. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3754. use Symfony\Component\HttpFoundation\Request;
  3755. use Symfony\Component\HttpFoundation\Response;
  3756. class FilterResponseEvent extends KernelEvent
  3757. {
  3758. private $response;
  3759. public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response)
  3760. {
  3761. parent::__construct($kernel, $request, $requestType);
  3762. $this->setResponse($response);
  3763. }
  3764. public function getResponse()
  3765. {
  3766. return $this->response;
  3767. }
  3768. public function setResponse(Response $response)
  3769. {
  3770. $this->response = $response;
  3771. }
  3772. }
  3773. }
  3774. namespace Symfony\Component\HttpKernel\Event
  3775. {
  3776. use Symfony\Component\HttpFoundation\Response;
  3777. class GetResponseEvent extends KernelEvent
  3778. {
  3779. private $response;
  3780. public function getResponse()
  3781. {
  3782. return $this->response;
  3783. }
  3784. public function setResponse(Response $response)
  3785. {
  3786. $this->response = $response;
  3787. $this->stopPropagation();
  3788. }
  3789. public function hasResponse()
  3790. {
  3791. return null !== $this->response;
  3792. }
  3793. }
  3794. }
  3795. namespace Symfony\Component\HttpKernel\Event
  3796. {
  3797. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3798. use Symfony\Component\HttpFoundation\Request;
  3799. class GetResponseForControllerResultEvent extends GetResponseEvent
  3800. {
  3801. private $controllerResult;
  3802. public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult)
  3803. {
  3804. parent::__construct($kernel, $request, $requestType);
  3805. $this->controllerResult = $controllerResult;
  3806. }
  3807. public function getControllerResult()
  3808. {
  3809. return $this->controllerResult;
  3810. }
  3811. }
  3812. }
  3813. namespace Symfony\Component\HttpKernel\Event
  3814. {
  3815. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3816. use Symfony\Component\HttpFoundation\Request;
  3817. class GetResponseForExceptionEvent extends GetResponseEvent
  3818. {
  3819. private $exception;
  3820. public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e)
  3821. {
  3822. parent::__construct($kernel, $request, $requestType);
  3823. $this->setException($e);
  3824. }
  3825. public function getException()
  3826. {
  3827. return $this->exception;
  3828. }
  3829. public function setException(\Exception $exception)
  3830. {
  3831. $this->exception = $exception;
  3832. }
  3833. }
  3834. }
  3835. namespace Symfony\Component\HttpKernel
  3836. {
  3837. final class KernelEvents
  3838. {
  3839. const REQUEST = 'kernel.request';
  3840. const EXCEPTION = 'kernel.exception';
  3841. const VIEW = 'kernel.view';
  3842. const CONTROLLER = 'kernel.controller';
  3843. const RESPONSE = 'kernel.response';
  3844. const TERMINATE = 'kernel.terminate';
  3845. }
  3846. }
  3847. namespace Symfony\Component\HttpKernel\Config
  3848. {
  3849. use Symfony\Component\Config\FileLocator as BaseFileLocator;
  3850. use Symfony\Component\HttpKernel\KernelInterface;
  3851. class FileLocator extends BaseFileLocator
  3852. {
  3853. private $kernel;
  3854. private $path;
  3855. public function __construct(KernelInterface $kernel, $path = null, array $paths = array())
  3856. {
  3857. $this->kernel = $kernel;
  3858. $this->path = $path;
  3859. $paths[] = $path;
  3860. parent::__construct($paths);
  3861. }
  3862. public function locate($file, $currentPath = null, $first = true)
  3863. {
  3864. if ('@' === $file[0]) {
  3865. return $this->kernel->locateResource($file, $this->path, $first);
  3866. }
  3867. return parent::locate($file, $currentPath, $first);
  3868. }
  3869. }
  3870. }
  3871. namespace Symfony\Bundle\FrameworkBundle\Controller
  3872. {
  3873. use Symfony\Component\HttpKernel\KernelInterface;
  3874. class ControllerNameParser
  3875. {
  3876. protected $kernel;
  3877. public function __construct(KernelInterface $kernel)
  3878. {
  3879. $this->kernel = $kernel;
  3880. }
  3881. public function parse($controller)
  3882. {
  3883. if (3 != count($parts = explode(':', $controller))) {
  3884. throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid a:b:c controller string.', $controller));
  3885. }
  3886. list($bundle, $controller, $action) = $parts;
  3887. $controller = str_replace('/', '\\', $controller);
  3888. $bundles = array();
  3889. foreach ($this->kernel->getBundle($bundle, false) as $b) {
  3890. $try = $b->getNamespace().'\\Controller\\'.$controller.'Controller';
  3891. if (class_exists($try)) {
  3892. return $try.'::'.$action.'Action';
  3893. }
  3894. $bundles[] = $b->getName();
  3895. $msg = sprintf('Unable to find controller "%s:%s" - class "%s" does not exist.', $bundle, $controller, $try);
  3896. }
  3897. if (count($bundles) > 1) {
  3898. $msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $bundles));
  3899. }
  3900. throw new \InvalidArgumentException($msg);
  3901. }
  3902. }
  3903. }
  3904. namespace Symfony\Bundle\FrameworkBundle\Controller
  3905. {
  3906. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  3907. use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver;
  3908. use Symfony\Component\DependencyInjection\ContainerInterface;
  3909. use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
  3910. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  3911. class ControllerResolver extends BaseControllerResolver
  3912. {
  3913. protected $container;
  3914. protected $parser;
  3915. public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null)
  3916. {
  3917. $this->container = $container;
  3918. $this->parser = $parser;
  3919. parent::__construct($logger);
  3920. }
  3921. protected function createController($controller)
  3922. {
  3923. if (false === strpos($controller, '::')) {
  3924. $count = substr_count($controller, ':');
  3925. if (2 == $count) {
  3926. $controller = $this->parser->parse($controller);
  3927. } elseif (1 == $count) {
  3928. list($service, $method) = explode(':', $controller, 2);
  3929. return array($this->container->get($service), $method);
  3930. } else {
  3931. throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
  3932. }
  3933. }
  3934. list($class, $method) = explode('::', $controller, 2);
  3935. if (!class_exists($class)) {
  3936. throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
  3937. }
  3938. $controller = new $class();
  3939. if ($controller instanceof ContainerAwareInterface) {
  3940. $controller->setContainer($this->container);
  3941. }
  3942. return array($controller, $method);
  3943. }
  3944. }
  3945. }
  3946. namespace Symfony\Component\Security\Http
  3947. {
  3948. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3949. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  3950. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  3951. class Firewall
  3952. {
  3953. private $map;
  3954. private $dispatcher;
  3955. public function __construct(FirewallMapInterface $map, EventDispatcherInterface $dispatcher)
  3956. {
  3957. $this->map = $map;
  3958. $this->dispatcher = $dispatcher;
  3959. }
  3960. public function onKernelRequest(GetResponseEvent $event)
  3961. {
  3962. if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  3963. return;
  3964. }
  3965. list($listeners, $exception) = $this->map->getListeners($event->getRequest());
  3966. if (null !== $exception) {
  3967. $exception->register($this->dispatcher);
  3968. }
  3969. foreach ($listeners as $listener) {
  3970. $response = $listener->handle($event);
  3971. if ($event->hasResponse()) {
  3972. break;
  3973. }
  3974. }
  3975. }
  3976. }
  3977. }
  3978. namespace Symfony\Component\Security\Core
  3979. {
  3980. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  3981. interface SecurityContextInterface
  3982. {
  3983. const ACCESS_DENIED_ERROR = '_security.403_error';
  3984. const AUTHENTICATION_ERROR = '_security.last_error';
  3985. const LAST_USERNAME = '_security.last_username';
  3986. public function getToken();
  3987. public function setToken(TokenInterface $token = null);
  3988. public function isGranted($attributes, $object = null);
  3989. }
  3990. }
  3991. namespace Symfony\Component\Security\Core
  3992. {
  3993. use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
  3994. use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
  3995. use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
  3996. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  3997. class SecurityContext implements SecurityContextInterface
  3998. {
  3999. private $token;
  4000. private $accessDecisionManager;
  4001. private $authenticationManager;
  4002. private $alwaysAuthenticate;
  4003. public function __construct(AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false)
  4004. {
  4005. $this->authenticationManager = $authenticationManager;
  4006. $this->accessDecisionManager = $accessDecisionManager;
  4007. $this->alwaysAuthenticate = $alwaysAuthenticate;
  4008. }
  4009. final public function isGranted($attributes, $object = null)
  4010. {
  4011. if (null === $this->token) {
  4012. throw new AuthenticationCredentialsNotFoundException('The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.');
  4013. }
  4014. if ($this->alwaysAuthenticate || !$this->token->isAuthenticated()) {
  4015. $this->token = $this->authenticationManager->authenticate($this->token);
  4016. }
  4017. if (!is_array($attributes)) {
  4018. $attributes = array($attributes);
  4019. }
  4020. return $this->accessDecisionManager->decide($this->token, $attributes, $object);
  4021. }
  4022. public function getToken()
  4023. {
  4024. return $this->token;
  4025. }
  4026. public function setToken(TokenInterface $token = null)
  4027. {
  4028. $this->token = $token;
  4029. }
  4030. }
  4031. }
  4032. namespace Symfony\Component\Security\Core\User
  4033. {
  4034. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  4035. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  4036. interface UserProviderInterface
  4037. {
  4038. public function loadUserByUsername($username);
  4039. public function refreshUser(UserInterface $user);
  4040. public function supportsClass($class);
  4041. }
  4042. }
  4043. namespace Symfony\Component\Security\Core\Authentication
  4044. {
  4045. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4046. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  4047. interface AuthenticationManagerInterface
  4048. {
  4049. public function authenticate(TokenInterface $token);
  4050. }
  4051. }
  4052. namespace Symfony\Component\Security\Core\Authentication
  4053. {
  4054. use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
  4055. use Symfony\Component\Security\Core\Event\AuthenticationEvent;
  4056. use Symfony\Component\Security\Core\AuthenticationEvents;
  4057. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  4058. use Symfony\Component\Security\Core\Exception\AccountStatusException;
  4059. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  4060. use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
  4061. use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
  4062. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4063. class AuthenticationProviderManager implements AuthenticationManagerInterface
  4064. {
  4065. private $providers;
  4066. private $eraseCredentials;
  4067. private $eventDispatcher;
  4068. public function __construct(array $providers, $eraseCredentials = true)
  4069. {
  4070. if (!$providers) {
  4071. throw new \InvalidArgumentException('You must at least add one authentication provider.');
  4072. }
  4073. $this->providers = $providers;
  4074. $this->eraseCredentials = (Boolean) $eraseCredentials;
  4075. }
  4076. public function setEventDispatcher(EventDispatcherInterface $dispatcher)
  4077. {
  4078. $this->eventDispatcher = $dispatcher;
  4079. }
  4080. public function authenticate(TokenInterface $token)
  4081. {
  4082. $lastException = null;
  4083. $result = null;
  4084. foreach ($this->providers as $provider) {
  4085. if (!$provider->supports($token)) {
  4086. continue;
  4087. }
  4088. try {
  4089. $result = $provider->authenticate($token);
  4090. if (null !== $result) {
  4091. break;
  4092. }
  4093. } catch (AccountStatusException $e) {
  4094. $e->setExtraInformation($token);
  4095. throw $e;
  4096. } catch (AuthenticationException $e) {
  4097. $lastException = $e;
  4098. }
  4099. }
  4100. if (null !== $result) {
  4101. if (true === $this->eraseCredentials) {
  4102. $result->eraseCredentials();
  4103. }
  4104. if (null !== $this->eventDispatcher) {
  4105. $this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_SUCCESS, new AuthenticationEvent($result));
  4106. }
  4107. return $result;
  4108. }
  4109. if (null === $lastException) {
  4110. $lastException = new ProviderNotFoundException(sprintf('No Authentication Provider found for token of class "%s".', get_class($token)));
  4111. }
  4112. if (null !== $this->eventDispatcher) {
  4113. $this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_FAILURE, new AuthenticationFailureEvent($token, $lastException));
  4114. }
  4115. $lastException->setExtraInformation($token);
  4116. throw $lastException;
  4117. }
  4118. }
  4119. }
  4120. namespace Symfony\Component\Security\Core\Authorization
  4121. {
  4122. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4123. interface AccessDecisionManagerInterface
  4124. {
  4125. public function decide(TokenInterface $token, array $attributes, $object = null);
  4126. public function supportsAttribute($attribute);
  4127. public function supportsClass($class);
  4128. }
  4129. }
  4130. namespace Symfony\Component\Security\Core\Authorization
  4131. {
  4132. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  4133. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4134. class AccessDecisionManager implements AccessDecisionManagerInterface
  4135. {
  4136. private $voters;
  4137. private $strategy;
  4138. private $allowIfAllAbstainDecisions;
  4139. private $allowIfEqualGrantedDeniedDecisions;
  4140. public function __construct(array $voters, $strategy = 'affirmative', $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true)
  4141. {
  4142. if (!$voters) {
  4143. throw new \InvalidArgumentException('You must at least add one voter.');
  4144. }
  4145. $this->voters = $voters;
  4146. $this->strategy = 'decide'.ucfirst($strategy);
  4147. $this->allowIfAllAbstainDecisions = (Boolean) $allowIfAllAbstainDecisions;
  4148. $this->allowIfEqualGrantedDeniedDecisions = (Boolean) $allowIfEqualGrantedDeniedDecisions;
  4149. }
  4150. public function decide(TokenInterface $token, array $attributes, $object = null)
  4151. {
  4152. return $this->{$this->strategy}($token, $attributes, $object);
  4153. }
  4154. public function supportsAttribute($attribute)
  4155. {
  4156. foreach ($this->voters as $voter) {
  4157. if ($voter->supportsAttribute($attribute)) {
  4158. return true;
  4159. }
  4160. }
  4161. return false;
  4162. }
  4163. public function supportsClass($class)
  4164. {
  4165. foreach ($this->voters as $voter) {
  4166. if ($voter->supportsClass($class)) {
  4167. return true;
  4168. }
  4169. }
  4170. return false;
  4171. }
  4172. private function decideAffirmative(TokenInterface $token, array $attributes, $object = null)
  4173. {
  4174. $deny = 0;
  4175. foreach ($this->voters as $voter) {
  4176. $result = $voter->vote($token, $object, $attributes);
  4177. switch ($result) {
  4178. case VoterInterface::ACCESS_GRANTED:
  4179. return true;
  4180. case VoterInterface::ACCESS_DENIED:
  4181. ++$deny;
  4182. break;
  4183. default:
  4184. break;
  4185. }
  4186. }
  4187. if ($deny > 0) {
  4188. return false;
  4189. }
  4190. return $this->allowIfAllAbstainDecisions;
  4191. }
  4192. private function decideConsensus(TokenInterface $token, array $attributes, $object = null)
  4193. {
  4194. $grant = 0;
  4195. $deny = 0;
  4196. $abstain = 0;
  4197. foreach ($this->voters as $voter) {
  4198. $result = $voter->vote($token, $object, $attributes);
  4199. switch ($result) {
  4200. case VoterInterface::ACCESS_GRANTED:
  4201. ++$grant;
  4202. break;
  4203. case VoterInterface::ACCESS_DENIED:
  4204. ++$deny;
  4205. break;
  4206. default:
  4207. ++$abstain;
  4208. break;
  4209. }
  4210. }
  4211. if ($grant > $deny) {
  4212. return true;
  4213. }
  4214. if ($deny > $grant) {
  4215. return false;
  4216. }
  4217. if ($grant == $deny && $grant != 0) {
  4218. return $this->allowIfEqualGrantedDeniedDecisions;
  4219. }
  4220. return $this->allowIfAllAbstainDecisions;
  4221. }
  4222. private function decideUnanimous(TokenInterface $token, array $attributes, $object = null)
  4223. {
  4224. $grant = 0;
  4225. foreach ($attributes as $attribute) {
  4226. foreach ($this->voters as $voter) {
  4227. $result = $voter->vote($token, $object, array($attribute));
  4228. switch ($result) {
  4229. case VoterInterface::ACCESS_GRANTED:
  4230. ++$grant;
  4231. break;
  4232. case VoterInterface::ACCESS_DENIED:
  4233. return false;
  4234. default:
  4235. break;
  4236. }
  4237. }
  4238. }
  4239. if ($grant > 0) {
  4240. return true;
  4241. }
  4242. return $this->allowIfAllAbstainDecisions;
  4243. }
  4244. }
  4245. }
  4246. namespace Symfony\Component\Security\Core\Authorization\Voter
  4247. {
  4248. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4249. interface VoterInterface
  4250. {
  4251. const ACCESS_GRANTED = 1;
  4252. const ACCESS_ABSTAIN = 0;
  4253. const ACCESS_DENIED = -1;
  4254. public function supportsAttribute($attribute);
  4255. public function supportsClass($class);
  4256. public function vote(TokenInterface $token, $object, array $attributes);
  4257. }
  4258. }
  4259. namespace Symfony\Component\Security\Http
  4260. {
  4261. use Symfony\Component\HttpFoundation\Request;
  4262. interface FirewallMapInterface
  4263. {
  4264. public function getListeners(Request $request);
  4265. }
  4266. }
  4267. namespace Symfony\Bundle\SecurityBundle\Security
  4268. {
  4269. use Symfony\Component\Security\Http\FirewallMapInterface;
  4270. use Symfony\Component\HttpFoundation\Request;
  4271. use Symfony\Component\DependencyInjection\ContainerInterface;
  4272. class FirewallMap implements FirewallMapInterface
  4273. {
  4274. protected $container;
  4275. protected $map;
  4276. public function __construct(ContainerInterface $container, array $map)
  4277. {
  4278. $this->container = $container;
  4279. $this->map = $map;
  4280. }
  4281. public function getListeners(Request $request)
  4282. {
  4283. foreach ($this->map as $contextId => $requestMatcher) {
  4284. if (null === $requestMatcher || $requestMatcher->matches($request)) {
  4285. return $this->container->get($contextId)->getContext();
  4286. }
  4287. }
  4288. return array(array(), null);
  4289. }
  4290. }
  4291. }
  4292. namespace Symfony\Bundle\SecurityBundle\Security
  4293. {
  4294. use Symfony\Component\Security\Http\Firewall\ExceptionListener;
  4295. class FirewallContext
  4296. {
  4297. private $listeners;
  4298. private $exceptionListener;
  4299. public function __construct(array $listeners, ExceptionListener $exceptionListener = null)
  4300. {
  4301. $this->listeners = $listeners;
  4302. $this->exceptionListener = $exceptionListener;
  4303. }
  4304. public function getContext()
  4305. {
  4306. return array($this->listeners, $this->exceptionListener);
  4307. }
  4308. }
  4309. }
  4310. namespace Symfony\Component\HttpFoundation
  4311. {
  4312. interface RequestMatcherInterface
  4313. {
  4314. public function matches(Request $request);
  4315. }
  4316. }
  4317. namespace Symfony\Component\HttpFoundation
  4318. {
  4319. class RequestMatcher implements RequestMatcherInterface
  4320. {
  4321. private $path;
  4322. private $host;
  4323. private $methods = array();
  4324. private $ip;
  4325. private $attributes = array();
  4326. public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
  4327. {
  4328. $this->matchPath($path);
  4329. $this->matchHost($host);
  4330. $this->matchMethod($methods);
  4331. $this->matchIp($ip);
  4332. foreach ($attributes as $k => $v) {
  4333. $this->matchAttribute($k, $v);
  4334. }
  4335. }
  4336. public function matchHost($regexp)
  4337. {
  4338. $this->host = $regexp;
  4339. }
  4340. public function matchPath($regexp)
  4341. {
  4342. $this->path = $regexp;
  4343. }
  4344. public function matchIp($ip)
  4345. {
  4346. $this->ip = $ip;
  4347. }
  4348. public function matchMethod($method)
  4349. {
  4350. $this->methods = array_map('strtoupper', (array) $method);
  4351. }
  4352. public function matchAttribute($key, $regexp)
  4353. {
  4354. $this->attributes[$key] = $regexp;
  4355. }
  4356. public function matches(Request $request)
  4357. {
  4358. if ($this->methods && !in_array($request->getMethod(), $this->methods)) {
  4359. return false;
  4360. }
  4361. foreach ($this->attributes as $key => $pattern) {
  4362. if (!preg_match('#'.str_replace('#', '\\#', $pattern).'#', $request->attributes->get($key))) {
  4363. return false;
  4364. }
  4365. }
  4366. if (null !== $this->path) {
  4367. $path = str_replace('#', '\\#', $this->path);
  4368. if (!preg_match('#'.$path.'#', rawurldecode($request->getPathInfo()))) {
  4369. return false;
  4370. }
  4371. }
  4372. if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#i', $request->getHost())) {
  4373. return false;
  4374. }
  4375. if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) {
  4376. return false;
  4377. }
  4378. return true;
  4379. }
  4380. protected function checkIp($requestIp, $ip)
  4381. {
  4382. if (false !== strpos($requestIp, ':')) {
  4383. return $this->checkIp6($requestIp, $ip);
  4384. } else {
  4385. return $this->checkIp4($requestIp, $ip);
  4386. }
  4387. }
  4388. protected function checkIp4($requestIp, $ip)
  4389. {
  4390. if (false !== strpos($ip, '/')) {
  4391. list($address, $netmask) = explode('/', $ip, 2);
  4392. if ($netmask < 1 || $netmask > 32) {
  4393. return false;
  4394. }
  4395. } else {
  4396. $address = $ip;
  4397. $netmask = 32;
  4398. }
  4399. return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
  4400. }
  4401. protected function checkIp6($requestIp, $ip)
  4402. {
  4403. if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
  4404. throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
  4405. }
  4406. if (false !== strpos($ip, '/')) {
  4407. list($address, $netmask) = explode('/', $ip, 2);
  4408. if ($netmask < 1 || $netmask > 128) {
  4409. return false;
  4410. }
  4411. } else {
  4412. $address = $ip;
  4413. $netmask = 128;
  4414. }
  4415. $bytesAddr = unpack("n*", inet_pton($address));
  4416. $bytesTest = unpack("n*", inet_pton($requestIp));
  4417. for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
  4418. $left = $netmask - 16 * ($i-1);
  4419. $left = ($left <= 16) ? $left : 16;
  4420. $mask = ~(0xffff >> $left) & 0xffff;
  4421. if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
  4422. return false;
  4423. }
  4424. }
  4425. return true;
  4426. }
  4427. }
  4428. }
  4429. namespace
  4430. {
  4431. /*
  4432. * This file is part of Twig.
  4433. *
  4434. * (c) 2009 Fabien Potencier
  4435. *
  4436. * For the full copyright and license information, please view the LICENSE
  4437. * file that was distributed with this source code.
  4438. */
  4439. /**
  4440. * Stores the Twig configuration.
  4441. *
  4442. * @package twig
  4443. * @author Fabien Potencier <fabien@symfony.com>
  4444. */
  4445. class Twig_Environment
  4446. {
  4447. const VERSION = '1.12.1';
  4448. protected $charset;
  4449. protected $loader;
  4450. protected $debug;
  4451. protected $autoReload;
  4452. protected $cache;
  4453. protected $lexer;
  4454. protected $parser;
  4455. protected $compiler;
  4456. protected $baseTemplateClass;
  4457. protected $extensions;
  4458. protected $parsers;
  4459. protected $visitors;
  4460. protected $filters;
  4461. protected $tests;
  4462. protected $functions;
  4463. protected $globals;
  4464. protected $runtimeInitialized;
  4465. protected $extensionInitialized;
  4466. protected $loadedTemplates;
  4467. protected $strictVariables;
  4468. protected $unaryOperators;
  4469. protected $binaryOperators;
  4470. protected $templateClassPrefix = '__TwigTemplate_';
  4471. protected $functionCallbacks;
  4472. protected $filterCallbacks;
  4473. protected $staging;
  4474. /**
  4475. * Constructor.
  4476. *
  4477. * Available options:
  4478. *
  4479. * * debug: When set to true, it automatically set "auto_reload" to true as
  4480. * well (default to false).
  4481. *
  4482. * * charset: The charset used by the templates (default to utf-8).
  4483. *
  4484. * * base_template_class: The base template class to use for generated
  4485. * templates (default to Twig_Template).
  4486. *
  4487. * * cache: An absolute path where to store the compiled templates, or
  4488. * false to disable compilation cache (default).
  4489. *
  4490. * * auto_reload: Whether to reload the template is the original source changed.
  4491. * If you don't provide the auto_reload option, it will be
  4492. * determined automatically base on the debug value.
  4493. *
  4494. * * strict_variables: Whether to ignore invalid variables in templates
  4495. * (default to false).
  4496. *
  4497. * * autoescape: Whether to enable auto-escaping (default to html):
  4498. * * false: disable auto-escaping
  4499. * * true: equivalent to html
  4500. * * html, js: set the autoescaping to one of the supported strategies
  4501. * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
  4502. *
  4503. * * optimizations: A flag that indicates which optimizations to apply
  4504. * (default to -1 which means that all optimizations are enabled;
  4505. * set it to 0 to disable).
  4506. *
  4507. * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
  4508. * @param array $options An array of options
  4509. */
  4510. public function __construct(Twig_LoaderInterface $loader = null, $options = array())
  4511. {
  4512. if (null !== $loader) {
  4513. $this->setLoader($loader);
  4514. }
  4515. $options = array_merge(array(
  4516. 'debug' => false,
  4517. 'charset' => 'UTF-8',
  4518. 'base_template_class' => 'Twig_Template',
  4519. 'strict_variables' => false,
  4520. 'autoescape' => 'html',
  4521. 'cache' => false,
  4522. 'auto_reload' => null,
  4523. 'optimizations' => -1,
  4524. ), $options);
  4525. $this->debug = (bool) $options['debug'];
  4526. $this->charset = $options['charset'];
  4527. $this->baseTemplateClass = $options['base_template_class'];
  4528. $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
  4529. $this->strictVariables = (bool) $options['strict_variables'];
  4530. $this->runtimeInitialized = false;
  4531. $this->setCache($options['cache']);
  4532. $this->functionCallbacks = array();
  4533. $this->filterCallbacks = array();
  4534. $this->addExtension(new Twig_Extension_Core());
  4535. $this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
  4536. $this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
  4537. $this->extensionInitialized = false;
  4538. $this->staging = new Twig_Extension_Staging();
  4539. }
  4540. /**
  4541. * Gets the base template class for compiled templates.
  4542. *
  4543. * @return string The base template class name
  4544. */
  4545. public function getBaseTemplateClass()
  4546. {
  4547. return $this->baseTemplateClass;
  4548. }
  4549. /**
  4550. * Sets the base template class for compiled templates.
  4551. *
  4552. * @param string $class The base template class name
  4553. */
  4554. public function setBaseTemplateClass($class)
  4555. {
  4556. $this->baseTemplateClass = $class;
  4557. }
  4558. /**
  4559. * Enables debugging mode.
  4560. */
  4561. public function enableDebug()
  4562. {
  4563. $this->debug = true;
  4564. }
  4565. /**
  4566. * Disables debugging mode.
  4567. */
  4568. public function disableDebug()
  4569. {
  4570. $this->debug = false;
  4571. }
  4572. /**
  4573. * Checks if debug mode is enabled.
  4574. *
  4575. * @return Boolean true if debug mode is enabled, false otherwise
  4576. */
  4577. public function isDebug()
  4578. {
  4579. return $this->debug;
  4580. }
  4581. /**
  4582. * Enables the auto_reload option.
  4583. */
  4584. public function enableAutoReload()
  4585. {
  4586. $this->autoReload = true;
  4587. }
  4588. /**
  4589. * Disables the auto_reload option.
  4590. */
  4591. public function disableAutoReload()
  4592. {
  4593. $this->autoReload = false;
  4594. }
  4595. /**
  4596. * Checks if the auto_reload option is enabled.
  4597. *
  4598. * @return Boolean true if auto_reload is enabled, false otherwise
  4599. */
  4600. public function isAutoReload()
  4601. {
  4602. return $this->autoReload;
  4603. }
  4604. /**
  4605. * Enables the strict_variables option.
  4606. */
  4607. public function enableStrictVariables()
  4608. {
  4609. $this->strictVariables = true;
  4610. }
  4611. /**
  4612. * Disables the strict_variables option.
  4613. */
  4614. public function disableStrictVariables()
  4615. {
  4616. $this->strictVariables = false;
  4617. }
  4618. /**
  4619. * Checks if the strict_variables option is enabled.
  4620. *
  4621. * @return Boolean true if strict_variables is enabled, false otherwise
  4622. */
  4623. public function isStrictVariables()
  4624. {
  4625. return $this->strictVariables;
  4626. }
  4627. /**
  4628. * Gets the cache directory or false if cache is disabled.
  4629. *
  4630. * @return string|false
  4631. */
  4632. public function getCache()
  4633. {
  4634. return $this->cache;
  4635. }
  4636. /**
  4637. * Sets the cache directory or false if cache is disabled.
  4638. *
  4639. * @param string|false $cache The absolute path to the compiled templates,
  4640. * or false to disable cache
  4641. */
  4642. public function setCache($cache)
  4643. {
  4644. $this->cache = $cache ? $cache : false;
  4645. }
  4646. /**
  4647. * Gets the cache filename for a given template.
  4648. *
  4649. * @param string $name The template name
  4650. *
  4651. * @return string The cache file name
  4652. */
  4653. public function getCacheFilename($name)
  4654. {
  4655. if (false === $this->cache) {
  4656. return false;
  4657. }
  4658. $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
  4659. return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
  4660. }
  4661. /**
  4662. * Gets the template class associated with the given string.
  4663. *
  4664. * @param string $name The name for which to calculate the template class name
  4665. * @param integer $index The index if it is an embedded template
  4666. *
  4667. * @return string The template class name
  4668. */
  4669. public function getTemplateClass($name, $index = null)
  4670. {
  4671. return $this->templateClassPrefix.md5($this->loader->getCacheKey($name)).(null === $index ? '' : '_'.$index);
  4672. }
  4673. /**
  4674. * Gets the template class prefix.
  4675. *
  4676. * @return string The template class prefix
  4677. */
  4678. public function getTemplateClassPrefix()
  4679. {
  4680. return $this->templateClassPrefix;
  4681. }
  4682. /**
  4683. * Renders a template.
  4684. *
  4685. * @param string $name The template name
  4686. * @param array $context An array of parameters to pass to the template
  4687. *
  4688. * @return string The rendered template
  4689. */
  4690. public function render($name, array $context = array())
  4691. {
  4692. return $this->loadTemplate($name)->render($context);
  4693. }
  4694. /**
  4695. * Displays a template.
  4696. *
  4697. * @param string $name The template name
  4698. * @param array $context An array of parameters to pass to the template
  4699. */
  4700. public function display($name, array $context = array())
  4701. {
  4702. $this->loadTemplate($name)->display($context);
  4703. }
  4704. /**
  4705. * Loads a template by name.
  4706. *
  4707. * @param string $name The template name
  4708. * @param integer $index The index if it is an embedded template
  4709. *
  4710. * @return Twig_TemplateInterface A template instance representing the given template name
  4711. */
  4712. public function loadTemplate($name, $index = null)
  4713. {
  4714. $cls = $this->getTemplateClass($name, $index);
  4715. if (isset($this->loadedTemplates[$cls])) {
  4716. return $this->loadedTemplates[$cls];
  4717. }
  4718. if (!class_exists($cls, false)) {
  4719. if (false === $cache = $this->getCacheFilename($name)) {
  4720. eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
  4721. } else {
  4722. if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
  4723. $this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
  4724. }
  4725. require_once $cache;
  4726. }
  4727. }
  4728. if (!$this->runtimeInitialized) {
  4729. $this->initRuntime();
  4730. }
  4731. return $this->loadedTemplates[$cls] = new $cls($this);
  4732. }
  4733. /**
  4734. * Returns true if the template is still fresh.
  4735. *
  4736. * Besides checking the loader for freshness information,
  4737. * this method also checks if the enabled extensions have
  4738. * not changed.
  4739. *
  4740. * @param string $name The template name
  4741. * @param timestamp $time The last modification time of the cached template
  4742. *
  4743. * @return Boolean true if the template is fresh, false otherwise
  4744. */
  4745. public function isTemplateFresh($name, $time)
  4746. {
  4747. foreach ($this->extensions as $extension) {
  4748. $r = new ReflectionObject($extension);
  4749. if (filemtime($r->getFileName()) > $time) {
  4750. return false;
  4751. }
  4752. }
  4753. return $this->loader->isFresh($name, $time);
  4754. }
  4755. public function resolveTemplate($names)
  4756. {
  4757. if (!is_array($names)) {
  4758. $names = array($names);
  4759. }
  4760. foreach ($names as $name) {
  4761. if ($name instanceof Twig_Template) {
  4762. return $name;
  4763. }
  4764. try {
  4765. return $this->loadTemplate($name);
  4766. } catch (Twig_Error_Loader $e) {
  4767. }
  4768. }
  4769. if (1 === count($names)) {
  4770. throw $e;
  4771. }
  4772. throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
  4773. }
  4774. /**
  4775. * Clears the internal template cache.
  4776. */
  4777. public function clearTemplateCache()
  4778. {
  4779. $this->loadedTemplates = array();
  4780. }
  4781. /**
  4782. * Clears the template cache files on the filesystem.
  4783. */
  4784. public function clearCacheFiles()
  4785. {
  4786. if (false === $this->cache) {
  4787. return;
  4788. }
  4789. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  4790. if ($file->isFile()) {
  4791. @unlink($file->getPathname());
  4792. }
  4793. }
  4794. }
  4795. /**
  4796. * Gets the Lexer instance.
  4797. *
  4798. * @return Twig_LexerInterface A Twig_LexerInterface instance
  4799. */
  4800. public function getLexer()
  4801. {
  4802. if (null === $this->lexer) {
  4803. $this->lexer = new Twig_Lexer($this);
  4804. }
  4805. return $this->lexer;
  4806. }
  4807. /**
  4808. * Sets the Lexer instance.
  4809. *
  4810. * @param Twig_LexerInterface A Twig_LexerInterface instance
  4811. */
  4812. public function setLexer(Twig_LexerInterface $lexer)
  4813. {
  4814. $this->lexer = $lexer;
  4815. }
  4816. /**
  4817. * Tokenizes a source code.
  4818. *
  4819. * @param string $source The template source code
  4820. * @param string $name The template name
  4821. *
  4822. * @return Twig_TokenStream A Twig_TokenStream instance
  4823. */
  4824. public function tokenize($source, $name = null)
  4825. {
  4826. return $this->getLexer()->tokenize($source, $name);
  4827. }
  4828. /**
  4829. * Gets the Parser instance.
  4830. *
  4831. * @return Twig_ParserInterface A Twig_ParserInterface instance
  4832. */
  4833. public function getParser()
  4834. {
  4835. if (null === $this->parser) {
  4836. $this->parser = new Twig_Parser($this);
  4837. }
  4838. return $this->parser;
  4839. }
  4840. /**
  4841. * Sets the Parser instance.
  4842. *
  4843. * @param Twig_ParserInterface A Twig_ParserInterface instance
  4844. */
  4845. public function setParser(Twig_ParserInterface $parser)
  4846. {
  4847. $this->parser = $parser;
  4848. }
  4849. /**
  4850. * Parses a token stream.
  4851. *
  4852. * @param Twig_TokenStream $tokens A Twig_TokenStream instance
  4853. *
  4854. * @return Twig_Node_Module A Node tree
  4855. */
  4856. public function parse(Twig_TokenStream $tokens)
  4857. {
  4858. return $this->getParser()->parse($tokens);
  4859. }
  4860. /**
  4861. * Gets the Compiler instance.
  4862. *
  4863. * @return Twig_CompilerInterface A Twig_CompilerInterface instance
  4864. */
  4865. public function getCompiler()
  4866. {
  4867. if (null === $this->compiler) {
  4868. $this->compiler = new Twig_Compiler($this);
  4869. }
  4870. return $this->compiler;
  4871. }
  4872. /**
  4873. * Sets the Compiler instance.
  4874. *
  4875. * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
  4876. */
  4877. public function setCompiler(Twig_CompilerInterface $compiler)
  4878. {
  4879. $this->compiler = $compiler;
  4880. }
  4881. /**
  4882. * Compiles a Node.
  4883. *
  4884. * @param Twig_NodeInterface $node A Twig_NodeInterface instance
  4885. *
  4886. * @return string The compiled PHP source code
  4887. */
  4888. public function compile(Twig_NodeInterface $node)
  4889. {
  4890. return $this->getCompiler()->compile($node)->getSource();
  4891. }
  4892. /**
  4893. * Compiles a template source code.
  4894. *
  4895. * @param string $source The template source code
  4896. * @param string $name The template name
  4897. *
  4898. * @return string The compiled PHP source code
  4899. */
  4900. public function compileSource($source, $name = null)
  4901. {
  4902. try {
  4903. return $this->compile($this->parse($this->tokenize($source, $name)));
  4904. } catch (Twig_Error $e) {
  4905. $e->setTemplateFile($name);
  4906. throw $e;
  4907. } catch (Exception $e) {
  4908. throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
  4909. }
  4910. }
  4911. /**
  4912. * Sets the Loader instance.
  4913. *
  4914. * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
  4915. */
  4916. public function setLoader(Twig_LoaderInterface $loader)
  4917. {
  4918. $this->loader = $loader;
  4919. }
  4920. /**
  4921. * Gets the Loader instance.
  4922. *
  4923. * @return Twig_LoaderInterface A Twig_LoaderInterface instance
  4924. */
  4925. public function getLoader()
  4926. {
  4927. return $this->loader;
  4928. }
  4929. /**
  4930. * Sets the default template charset.
  4931. *
  4932. * @param string $charset The default charset
  4933. */
  4934. public function setCharset($charset)
  4935. {
  4936. $this->charset = $charset;
  4937. }
  4938. /**
  4939. * Gets the default template charset.
  4940. *
  4941. * @return string The default charset
  4942. */
  4943. public function getCharset()
  4944. {
  4945. return $this->charset;
  4946. }
  4947. /**
  4948. * Initializes the runtime environment.
  4949. */
  4950. public function initRuntime()
  4951. {
  4952. $this->runtimeInitialized = true;
  4953. foreach ($this->getExtensions() as $extension) {
  4954. $extension->initRuntime($this);
  4955. }
  4956. }
  4957. /**
  4958. * Returns true if the given extension is registered.
  4959. *
  4960. * @param string $name The extension name
  4961. *
  4962. * @return Boolean Whether the extension is registered or not
  4963. */
  4964. public function hasExtension($name)
  4965. {
  4966. return isset($this->extensions[$name]);
  4967. }
  4968. /**
  4969. * Gets an extension by name.
  4970. *
  4971. * @param string $name The extension name
  4972. *
  4973. * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
  4974. */
  4975. public function getExtension($name)
  4976. {
  4977. if (!isset($this->extensions[$name])) {
  4978. throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
  4979. }
  4980. return $this->extensions[$name];
  4981. }
  4982. /**
  4983. * Registers an extension.
  4984. *
  4985. * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
  4986. */
  4987. public function addExtension(Twig_ExtensionInterface $extension)
  4988. {
  4989. if ($this->extensionInitialized) {
  4990. throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
  4991. }
  4992. $this->extensions[$extension->getName()] = $extension;
  4993. }
  4994. /**
  4995. * Removes an extension by name.
  4996. *
  4997. * This method is deprecated and you should not use it.
  4998. *
  4999. * @param string $name The extension name
  5000. *
  5001. * @deprecated since 1.12 (to be removed in 2.0)
  5002. */
  5003. public function removeExtension($name)
  5004. {
  5005. if ($this->extensionInitialized) {
  5006. throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
  5007. }
  5008. unset($this->extensions[$name]);
  5009. }
  5010. /**
  5011. * Registers an array of extensions.
  5012. *
  5013. * @param array $extensions An array of extensions
  5014. */
  5015. public function setExtensions(array $extensions)
  5016. {
  5017. foreach ($extensions as $extension) {
  5018. $this->addExtension($extension);
  5019. }
  5020. }
  5021. /**
  5022. * Returns all registered extensions.
  5023. *
  5024. * @return array An array of extensions
  5025. */
  5026. public function getExtensions()
  5027. {
  5028. return $this->extensions;
  5029. }
  5030. /**
  5031. * Registers a Token Parser.
  5032. *
  5033. * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
  5034. */
  5035. public function addTokenParser(Twig_TokenParserInterface $parser)
  5036. {
  5037. if ($this->extensionInitialized) {
  5038. throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
  5039. }
  5040. $this->staging->addTokenParser($parser);
  5041. }
  5042. /**
  5043. * Gets the registered Token Parsers.
  5044. *
  5045. * @return Twig_TokenParserBrokerInterface A broker containing token parsers
  5046. */
  5047. public function getTokenParsers()
  5048. {
  5049. if (!$this->extensionInitialized) {
  5050. $this->initExtensions();
  5051. }
  5052. return $this->parsers;
  5053. }
  5054. /**
  5055. * Gets registered tags.
  5056. *
  5057. * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
  5058. *
  5059. * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
  5060. */
  5061. public function getTags()
  5062. {
  5063. $tags = array();
  5064. foreach ($this->getTokenParsers()->getParsers() as $parser) {
  5065. if ($parser instanceof Twig_TokenParserInterface) {
  5066. $tags[$parser->getTag()] = $parser;
  5067. }
  5068. }
  5069. return $tags;
  5070. }
  5071. /**
  5072. * Registers a Node Visitor.
  5073. *
  5074. * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
  5075. */
  5076. public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
  5077. {
  5078. if ($this->extensionInitialized) {
  5079. throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName());
  5080. }
  5081. $this->staging->addNodeVisitor($visitor);
  5082. }
  5083. /**
  5084. * Gets the registered Node Visitors.
  5085. *
  5086. * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
  5087. */
  5088. public function getNodeVisitors()
  5089. {
  5090. if (!$this->extensionInitialized) {
  5091. $this->initExtensions();
  5092. }
  5093. return $this->visitors;
  5094. }
  5095. /**
  5096. * Registers a Filter.
  5097. *
  5098. * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
  5099. * @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance
  5100. */
  5101. public function addFilter($name, $filter = null)
  5102. {
  5103. if ($this->extensionInitialized) {
  5104. throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
  5105. }
  5106. if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
  5107. throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter');
  5108. }
  5109. if ($name instanceof Twig_SimpleFilter) {
  5110. $filter = $name;
  5111. $name = $filter->getName();
  5112. }
  5113. $this->staging->addFilter($name, $filter);
  5114. }
  5115. /**
  5116. * Get a filter by name.
  5117. *
  5118. * Subclasses may override this method and load filters differently;
  5119. * so no list of filters is available.
  5120. *
  5121. * @param string $name The filter name
  5122. *
  5123. * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist
  5124. */
  5125. public function getFilter($name)
  5126. {
  5127. if (!$this->extensionInitialized) {
  5128. $this->initExtensions();
  5129. }
  5130. if (isset($this->filters[$name])) {
  5131. return $this->filters[$name];
  5132. }
  5133. foreach ($this->filters as $pattern => $filter) {
  5134. $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
  5135. if ($count) {
  5136. if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
  5137. array_shift($matches);
  5138. $filter->setArguments($matches);
  5139. return $filter;
  5140. }
  5141. }
  5142. }
  5143. foreach ($this->filterCallbacks as $callback) {
  5144. if (false !== $filter = call_user_func($callback, $name)) {
  5145. return $filter;
  5146. }
  5147. }
  5148. return false;
  5149. }
  5150. public function registerUndefinedFilterCallback($callable)
  5151. {
  5152. $this->filterCallbacks[] = $callable;
  5153. }
  5154. /**
  5155. * Gets the registered Filters.
  5156. *
  5157. * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
  5158. *
  5159. * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
  5160. *
  5161. * @see registerUndefinedFilterCallback
  5162. */
  5163. public function getFilters()
  5164. {
  5165. if (!$this->extensionInitialized) {
  5166. $this->initExtensions();
  5167. }
  5168. return $this->filters;
  5169. }
  5170. /**
  5171. * Registers a Test.
  5172. *
  5173. * @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance
  5174. * @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance
  5175. */
  5176. public function addTest($name, $test = null)
  5177. {
  5178. if ($this->extensionInitialized) {
  5179. throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
  5180. }
  5181. if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
  5182. throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest');
  5183. }
  5184. if ($name instanceof Twig_SimpleTest) {
  5185. $test = $name;
  5186. $name = $test->getName();
  5187. }
  5188. $this->staging->addTest($name, $test);
  5189. }
  5190. /**
  5191. * Gets the registered Tests.
  5192. *
  5193. * @return Twig_TestInterface[] An array of Twig_TestInterface instances
  5194. */
  5195. public function getTests()
  5196. {
  5197. if (!$this->extensionInitialized) {
  5198. $this->initExtensions();
  5199. }
  5200. return $this->tests;
  5201. }
  5202. /**
  5203. * Gets a test by name.
  5204. *
  5205. * @param string $name The test name
  5206. *
  5207. * @return Twig_Test|false A Twig_Test instance or false if the test does not exist
  5208. */
  5209. public function getTest($name)
  5210. {
  5211. if (!$this->extensionInitialized) {
  5212. $this->initExtensions();
  5213. }
  5214. if (isset($this->tests[$name])) {
  5215. return $this->tests[$name];
  5216. }
  5217. return false;
  5218. }
  5219. /**
  5220. * Registers a Function.
  5221. *
  5222. * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
  5223. * @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance
  5224. */
  5225. public function addFunction($name, $function = null)
  5226. {
  5227. if ($this->extensionInitialized) {
  5228. throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
  5229. }
  5230. if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
  5231. throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction');
  5232. }
  5233. if ($name instanceof Twig_SimpleFunction) {
  5234. $function = $name;
  5235. $name = $function->getName();
  5236. }
  5237. $this->staging->addFunction($name, $function);
  5238. }
  5239. /**
  5240. * Get a function by name.
  5241. *
  5242. * Subclasses may override this method and load functions differently;
  5243. * so no list of functions is available.
  5244. *
  5245. * @param string $name function name
  5246. *
  5247. * @return Twig_Function|false A Twig_Function instance or false if the function does not exist
  5248. */
  5249. public function getFunction($name)
  5250. {
  5251. if (!$this->extensionInitialized) {
  5252. $this->initExtensions();
  5253. }
  5254. if (isset($this->functions[$name])) {
  5255. return $this->functions[$name];
  5256. }
  5257. foreach ($this->functions as $pattern => $function) {
  5258. $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
  5259. if ($count) {
  5260. if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
  5261. array_shift($matches);
  5262. $function->setArguments($matches);
  5263. return $function;
  5264. }
  5265. }
  5266. }
  5267. foreach ($this->functionCallbacks as $callback) {
  5268. if (false !== $function = call_user_func($callback, $name)) {
  5269. return $function;
  5270. }
  5271. }
  5272. return false;
  5273. }
  5274. public function registerUndefinedFunctionCallback($callable)
  5275. {
  5276. $this->functionCallbacks[] = $callable;
  5277. }
  5278. /**
  5279. * Gets registered functions.
  5280. *
  5281. * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
  5282. *
  5283. * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
  5284. *
  5285. * @see registerUndefinedFunctionCallback
  5286. */
  5287. public function getFunctions()
  5288. {
  5289. if (!$this->extensionInitialized) {
  5290. $this->initExtensions();
  5291. }
  5292. return $this->functions;
  5293. }
  5294. /**
  5295. * Registers a Global.
  5296. *
  5297. * New globals can be added before compiling or rendering a template;
  5298. * but after, you can only update existing globals.
  5299. *
  5300. * @param string $name The global name
  5301. * @param mixed $value The global value
  5302. */
  5303. public function addGlobal($name, $value)
  5304. {
  5305. if ($this->extensionInitialized || $this->runtimeInitialized) {
  5306. if (null === $this->globals) {
  5307. $this->initGlobals();
  5308. }
  5309. /* This condition must be uncommented in Twig 2.0
  5310. if (!array_key_exists($name, $this->globals)) {
  5311. throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
  5312. }
  5313. */
  5314. }
  5315. if ($this->extensionInitialized || $this->runtimeInitialized) {
  5316. // update the value
  5317. $this->globals[$name] = $value;
  5318. } else {
  5319. $this->staging->addGlobal($name, $value);
  5320. }
  5321. }
  5322. /**
  5323. * Gets the registered Globals.
  5324. *
  5325. * @return array An array of globals
  5326. */
  5327. public function getGlobals()
  5328. {
  5329. if (null === $this->globals || !($this->runtimeInitialized || $this->extensionInitialized)) {
  5330. $this->initGlobals();
  5331. }
  5332. return $this->globals;
  5333. }
  5334. /**
  5335. * Merges a context with the defined globals.
  5336. *
  5337. * @param array $context An array representing the context
  5338. *
  5339. * @return array The context merged with the globals
  5340. */
  5341. public function mergeGlobals(array $context)
  5342. {
  5343. // we don't use array_merge as the context being generally
  5344. // bigger than globals, this code is faster.
  5345. foreach ($this->getGlobals() as $key => $value) {
  5346. if (!array_key_exists($key, $context)) {
  5347. $context[$key] = $value;
  5348. }
  5349. }
  5350. return $context;
  5351. }
  5352. /**
  5353. * Gets the registered unary Operators.
  5354. *
  5355. * @return array An array of unary operators
  5356. */
  5357. public function getUnaryOperators()
  5358. {
  5359. if (!$this->extensionInitialized) {
  5360. $this->initExtensions();
  5361. }
  5362. return $this->unaryOperators;
  5363. }
  5364. /**
  5365. * Gets the registered binary Operators.
  5366. *
  5367. * @return array An array of binary operators
  5368. */
  5369. public function getBinaryOperators()
  5370. {
  5371. if (!$this->extensionInitialized) {
  5372. $this->initExtensions();
  5373. }
  5374. return $this->binaryOperators;
  5375. }
  5376. public function computeAlternatives($name, $items)
  5377. {
  5378. $alternatives = array();
  5379. foreach ($items as $item) {
  5380. $lev = levenshtein($name, $item);
  5381. if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
  5382. $alternatives[$item] = $lev;
  5383. }
  5384. }
  5385. asort($alternatives);
  5386. return array_keys($alternatives);
  5387. }
  5388. protected function initGlobals()
  5389. {
  5390. $this->globals = array();
  5391. foreach ($this->extensions as $extension) {
  5392. $this->globals = array_merge($this->globals, $extension->getGlobals());
  5393. }
  5394. $this->globals = array_merge($this->globals, $this->staging->getGlobals());
  5395. }
  5396. protected function initExtensions()
  5397. {
  5398. if ($this->extensionInitialized) {
  5399. return;
  5400. }
  5401. $this->extensionInitialized = true;
  5402. $this->parsers = new Twig_TokenParserBroker();
  5403. $this->filters = array();
  5404. $this->functions = array();
  5405. $this->tests = array();
  5406. $this->visitors = array();
  5407. $this->unaryOperators = array();
  5408. $this->binaryOperators = array();
  5409. foreach ($this->extensions as $extension) {
  5410. $this->initExtension($extension);
  5411. }
  5412. $this->initExtension($this->staging);
  5413. }
  5414. protected function initExtension(Twig_ExtensionInterface $extension)
  5415. {
  5416. // filters
  5417. foreach ($extension->getFilters() as $name => $filter) {
  5418. if ($name instanceof Twig_SimpleFilter) {
  5419. $filter = $name;
  5420. $name = $filter->getName();
  5421. } elseif ($filter instanceof Twig_SimpleFilter) {
  5422. $name = $filter->getName();
  5423. }
  5424. $this->filters[$name] = $filter;
  5425. }
  5426. // functions
  5427. foreach ($extension->getFunctions() as $name => $function) {
  5428. if ($name instanceof Twig_SimpleFunction) {
  5429. $function = $name;
  5430. $name = $function->getName();
  5431. } elseif ($function instanceof Twig_SimpleFunction) {
  5432. $name = $function->getName();
  5433. }
  5434. $this->functions[$name] = $function;
  5435. }
  5436. // tests
  5437. foreach ($extension->getTests() as $name => $test) {
  5438. if ($name instanceof Twig_SimpleTest) {
  5439. $test = $name;
  5440. $name = $test->getName();
  5441. } elseif ($test instanceof Twig_SimpleTest) {
  5442. $name = $test->getName();
  5443. }
  5444. $this->tests[$name] = $test;
  5445. }
  5446. // token parsers
  5447. foreach ($extension->getTokenParsers() as $parser) {
  5448. if ($parser instanceof Twig_TokenParserInterface) {
  5449. $this->parsers->addTokenParser($parser);
  5450. } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
  5451. $this->parsers->addTokenParserBroker($parser);
  5452. } else {
  5453. throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
  5454. }
  5455. }
  5456. // node visitors
  5457. foreach ($extension->getNodeVisitors() as $visitor) {
  5458. $this->visitors[] = $visitor;
  5459. }
  5460. // operators
  5461. if ($operators = $extension->getOperators()) {
  5462. if (2 !== count($operators)) {
  5463. throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
  5464. }
  5465. $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
  5466. $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
  5467. }
  5468. }
  5469. protected function writeCacheFile($file, $content)
  5470. {
  5471. $dir = dirname($file);
  5472. if (!is_dir($dir)) {
  5473. if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
  5474. throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
  5475. }
  5476. } elseif (!is_writable($dir)) {
  5477. throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
  5478. }
  5479. $tmpFile = tempnam(dirname($file), basename($file));
  5480. if (false !== @file_put_contents($tmpFile, $content)) {
  5481. // rename does not work on Win32 before 5.2.6
  5482. if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
  5483. @chmod($file, 0666 & ~umask());
  5484. return;
  5485. }
  5486. }
  5487. throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
  5488. }
  5489. }
  5490. }
  5491. namespace
  5492. {
  5493. /*
  5494. * This file is part of Twig.
  5495. *
  5496. * (c) 2009 Fabien Potencier
  5497. *
  5498. * For the full copyright and license information, please view the LICENSE
  5499. * file that was distributed with this source code.
  5500. */
  5501. /**
  5502. * Interface implemented by extension classes.
  5503. *
  5504. * @package twig
  5505. * @author Fabien Potencier <fabien@symfony.com>
  5506. */
  5507. interface Twig_ExtensionInterface
  5508. {
  5509. /**
  5510. * Initializes the runtime environment.
  5511. *
  5512. * This is where you can load some file that contains filter functions for instance.
  5513. *
  5514. * @param Twig_Environment $environment The current Twig_Environment instance
  5515. */
  5516. public function initRuntime(Twig_Environment $environment);
  5517. /**
  5518. * Returns the token parser instances to add to the existing list.
  5519. *
  5520. * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
  5521. */
  5522. public function getTokenParsers();
  5523. /**
  5524. * Returns the node visitor instances to add to the existing list.
  5525. *
  5526. * @return array An array of Twig_NodeVisitorInterface instances
  5527. */
  5528. public function getNodeVisitors();
  5529. /**
  5530. * Returns a list of filters to add to the existing list.
  5531. *
  5532. * @return array An array of filters
  5533. */
  5534. public function getFilters();
  5535. /**
  5536. * Returns a list of tests to add to the existing list.
  5537. *
  5538. * @return array An array of tests
  5539. */
  5540. public function getTests();
  5541. /**
  5542. * Returns a list of functions to add to the existing list.
  5543. *
  5544. * @return array An array of functions
  5545. */
  5546. public function getFunctions();
  5547. /**
  5548. * Returns a list of operators to add to the existing list.
  5549. *
  5550. * @return array An array of operators
  5551. */
  5552. public function getOperators();
  5553. /**
  5554. * Returns a list of global variables to add to the existing list.
  5555. *
  5556. * @return array An array of global variables
  5557. */
  5558. public function getGlobals();
  5559. /**
  5560. * Returns the name of the extension.
  5561. *
  5562. * @return string The extension name
  5563. */
  5564. public function getName();
  5565. }
  5566. }
  5567. namespace
  5568. {
  5569. /*
  5570. * This file is part of Twig.
  5571. *
  5572. * (c) 2009 Fabien Potencier
  5573. *
  5574. * For the full copyright and license information, please view the LICENSE
  5575. * file that was distributed with this source code.
  5576. */
  5577. abstract class Twig_Extension implements Twig_ExtensionInterface
  5578. {
  5579. /**
  5580. * Initializes the runtime environment.
  5581. *
  5582. * This is where you can load some file that contains filter functions for instance.
  5583. *
  5584. * @param Twig_Environment $environment The current Twig_Environment instance
  5585. */
  5586. public function initRuntime(Twig_Environment $environment)
  5587. {
  5588. }
  5589. /**
  5590. * Returns the token parser instances to add to the existing list.
  5591. *
  5592. * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
  5593. */
  5594. public function getTokenParsers()
  5595. {
  5596. return array();
  5597. }
  5598. /**
  5599. * Returns the node visitor instances to add to the existing list.
  5600. *
  5601. * @return array An array of Twig_NodeVisitorInterface instances
  5602. */
  5603. public function getNodeVisitors()
  5604. {
  5605. return array();
  5606. }
  5607. /**
  5608. * Returns a list of filters to add to the existing list.
  5609. *
  5610. * @return array An array of filters
  5611. */
  5612. public function getFilters()
  5613. {
  5614. return array();
  5615. }
  5616. /**
  5617. * Returns a list of tests to add to the existing list.
  5618. *
  5619. * @return array An array of tests
  5620. */
  5621. public function getTests()
  5622. {
  5623. return array();
  5624. }
  5625. /**
  5626. * Returns a list of functions to add to the existing list.
  5627. *
  5628. * @return array An array of functions
  5629. */
  5630. public function getFunctions()
  5631. {
  5632. return array();
  5633. }
  5634. /**
  5635. * Returns a list of operators to add to the existing list.
  5636. *
  5637. * @return array An array of operators
  5638. */
  5639. public function getOperators()
  5640. {
  5641. return array();
  5642. }
  5643. /**
  5644. * Returns a list of global variables to add to the existing list.
  5645. *
  5646. * @return array An array of global variables
  5647. */
  5648. public function getGlobals()
  5649. {
  5650. return array();
  5651. }
  5652. }
  5653. }
  5654. namespace
  5655. {
  5656. if (!defined('ENT_SUBSTITUTE')) {
  5657. define('ENT_SUBSTITUTE', 8);
  5658. }
  5659. /*
  5660. * This file is part of Twig.
  5661. *
  5662. * (c) 2009 Fabien Potencier
  5663. *
  5664. * For the full copyright and license information, please view the LICENSE
  5665. * file that was distributed with this source code.
  5666. */
  5667. class Twig_Extension_Core extends Twig_Extension
  5668. {
  5669. protected $dateFormats = array('F j, Y H:i', '%d days');
  5670. protected $numberFormat = array(0, '.', ',');
  5671. protected $timezone = null;
  5672. /**
  5673. * Sets the default format to be used by the date filter.
  5674. *
  5675. * @param string $format The default date format string
  5676. * @param string $dateIntervalFormat The default date interval format string
  5677. */
  5678. public function setDateFormat($format = null, $dateIntervalFormat = null)
  5679. {
  5680. if (null !== $format) {
  5681. $this->dateFormats[0] = $format;
  5682. }
  5683. if (null !== $dateIntervalFormat) {
  5684. $this->dateFormats[1] = $dateIntervalFormat;
  5685. }
  5686. }
  5687. /**
  5688. * Gets the default format to be used by the date filter.
  5689. *
  5690. * @return array The default date format string and the default date interval format string
  5691. */
  5692. public function getDateFormat()
  5693. {
  5694. return $this->dateFormats;
  5695. }
  5696. /**
  5697. * Sets the default timezone to be used by the date filter.
  5698. *
  5699. * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object
  5700. */
  5701. public function setTimezone($timezone)
  5702. {
  5703. $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
  5704. }
  5705. /**
  5706. * Gets the default timezone to be used by the date filter.
  5707. *
  5708. * @return DateTimeZone The default timezone currently in use
  5709. */
  5710. public function getTimezone()
  5711. {
  5712. if (null === $this->timezone) {
  5713. $this->timezone = new DateTimeZone(date_default_timezone_get());
  5714. }
  5715. return $this->timezone;
  5716. }
  5717. /**
  5718. * Sets the default format to be used by the number_format filter.
  5719. *
  5720. * @param integer $decimal The number of decimal places to use.
  5721. * @param string $decimalPoint The character(s) to use for the decimal point.
  5722. * @param string $thousandSep The character(s) to use for the thousands separator.
  5723. */
  5724. public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
  5725. {
  5726. $this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
  5727. }
  5728. /**
  5729. * Get the default format used by the number_format filter.
  5730. *
  5731. * @return array The arguments for number_format()
  5732. */
  5733. public function getNumberFormat()
  5734. {
  5735. return $this->numberFormat;
  5736. }
  5737. /**
  5738. * Returns the token parser instance to add to the existing list.
  5739. *
  5740. * @return array An array of Twig_TokenParser instances
  5741. */
  5742. public function getTokenParsers()
  5743. {
  5744. return array(
  5745. new Twig_TokenParser_For(),
  5746. new Twig_TokenParser_If(),
  5747. new Twig_TokenParser_Extends(),
  5748. new Twig_TokenParser_Include(),
  5749. new Twig_TokenParser_Block(),
  5750. new Twig_TokenParser_Use(),
  5751. new Twig_TokenParser_Filter(),
  5752. new Twig_TokenParser_Macro(),
  5753. new Twig_TokenParser_Import(),
  5754. new Twig_TokenParser_From(),
  5755. new Twig_TokenParser_Set(),
  5756. new Twig_TokenParser_Spaceless(),
  5757. new Twig_TokenParser_Flush(),
  5758. new Twig_TokenParser_Do(),
  5759. new Twig_TokenParser_Embed(),
  5760. );
  5761. }
  5762. /**
  5763. * Returns a list of filters to add to the existing list.
  5764. *
  5765. * @return array An array of filters
  5766. */
  5767. public function getFilters()
  5768. {
  5769. $filters = array(
  5770. // formatting filters
  5771. new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)),
  5772. new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)),
  5773. new Twig_SimpleFilter('format', 'sprintf'),
  5774. new Twig_SimpleFilter('replace', 'strtr'),
  5775. new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
  5776. new Twig_SimpleFilter('abs', 'abs'),
  5777. // encoding
  5778. new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
  5779. new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'),
  5780. new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'),
  5781. // string filters
  5782. new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)),
  5783. new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)),
  5784. new Twig_SimpleFilter('upper', 'strtoupper'),
  5785. new Twig_SimpleFilter('lower', 'strtolower'),
  5786. new Twig_SimpleFilter('striptags', 'strip_tags'),
  5787. new Twig_SimpleFilter('trim', 'trim'),
  5788. new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
  5789. // array helpers
  5790. new Twig_SimpleFilter('join', 'twig_join_filter'),
  5791. new Twig_SimpleFilter('split', 'twig_split_filter'),
  5792. new Twig_SimpleFilter('sort', 'twig_sort_filter'),
  5793. new Twig_SimpleFilter('merge', 'twig_array_merge'),
  5794. // string/array filters
  5795. new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)),
  5796. new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)),
  5797. new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)),
  5798. // iteration and runtime
  5799. new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')),
  5800. new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'),
  5801. // escaping
  5802. new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
  5803. new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
  5804. );
  5805. if (function_exists('mb_get_info')) {
  5806. $filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true));
  5807. $filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true));
  5808. }
  5809. return $filters;
  5810. }
  5811. /**
  5812. * Returns a list of global functions to add to the existing list.
  5813. *
  5814. * @return array An array of global functions
  5815. */
  5816. public function getFunctions()
  5817. {
  5818. return array(
  5819. new Twig_SimpleFunction('range', 'range'),
  5820. new Twig_SimpleFunction('constant', 'twig_constant'),
  5821. new Twig_SimpleFunction('cycle', 'twig_cycle'),
  5822. new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
  5823. new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
  5824. new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true)),
  5825. );
  5826. }
  5827. /**
  5828. * Returns a list of tests to add to the existing list.
  5829. *
  5830. * @return array An array of tests
  5831. */
  5832. public function getTests()
  5833. {
  5834. return array(
  5835. new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
  5836. new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
  5837. new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
  5838. new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
  5839. new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
  5840. new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
  5841. new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
  5842. new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
  5843. new Twig_SimpleTest('empty', 'twig_test_empty'),
  5844. new Twig_SimpleTest('iterable', 'twig_test_iterable'),
  5845. );
  5846. }
  5847. /**
  5848. * Returns a list of operators to add to the existing list.
  5849. *
  5850. * @return array An array of operators
  5851. */
  5852. public function getOperators()
  5853. {
  5854. return array(
  5855. array(
  5856. 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
  5857. '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
  5858. '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
  5859. ),
  5860. array(
  5861. 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5862. 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5863. 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5864. 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5865. 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5866. '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5867. '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5868. '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5869. '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5870. '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5871. '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5872. 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5873. 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5874. '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5875. '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5876. '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5877. '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5878. '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5879. '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5880. '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5881. '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5882. 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5883. 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  5884. '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
  5885. ),
  5886. );
  5887. }
  5888. public function parseNotTestExpression(Twig_Parser $parser, $node)
  5889. {
  5890. return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
  5891. }
  5892. public function parseTestExpression(Twig_Parser $parser, $node)
  5893. {
  5894. $stream = $parser->getStream();
  5895. $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
  5896. $arguments = null;
  5897. if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
  5898. $arguments = $parser->getExpressionParser()->parseArguments(true);
  5899. }
  5900. $class = $this->getTestNodeClass($parser, $name, $node->getLine());
  5901. return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
  5902. }
  5903. protected function getTestNodeClass(Twig_Parser $parser, $name, $line)
  5904. {
  5905. $env = $parser->getEnvironment();
  5906. $testMap = $env->getTests();
  5907. if (!isset($testMap[$name])) {
  5908. $message = sprintf('The test "%s" does not exist', $name);
  5909. if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) {
  5910. $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
  5911. }
  5912. throw new Twig_Error_Syntax($message, $line, $parser->getFilename());
  5913. }
  5914. if ($testMap[$name] instanceof Twig_SimpleTest) {
  5915. return $testMap[$name]->getNodeClass();
  5916. }
  5917. return $testMap[$name] instanceof Twig_Test_Node ? $testMap[$name]->getClass() : 'Twig_Node_Expression_Test';
  5918. }
  5919. /**
  5920. * Returns the name of the extension.
  5921. *
  5922. * @return string The extension name
  5923. */
  5924. public function getName()
  5925. {
  5926. return 'core';
  5927. }
  5928. }
  5929. /**
  5930. * Cycles over a value.
  5931. *
  5932. * @param ArrayAccess|array $values An array or an ArrayAccess instance
  5933. * @param integer $position The cycle position
  5934. *
  5935. * @return string The next value in the cycle
  5936. */
  5937. function twig_cycle($values, $position)
  5938. {
  5939. if (!is_array($values) && !$values instanceof ArrayAccess) {
  5940. return $values;
  5941. }
  5942. return $values[$position % count($values)];
  5943. }
  5944. /**
  5945. * Returns a random value depending on the supplied parameter type:
  5946. * - a random item from a Traversable or array
  5947. * - a random character from a string
  5948. * - a random integer between 0 and the integer parameter
  5949. *
  5950. * @param Twig_Environment $env A Twig_Environment instance
  5951. * @param Traversable|array|integer|string $values The values to pick a random item from
  5952. *
  5953. * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
  5954. *
  5955. * @return mixed A random value from the given sequence
  5956. */
  5957. function twig_random(Twig_Environment $env, $values = null)
  5958. {
  5959. if (null === $values) {
  5960. return mt_rand();
  5961. }
  5962. if (is_int($values) || is_float($values)) {
  5963. return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
  5964. }
  5965. if ($values instanceof Traversable) {
  5966. $values = iterator_to_array($values);
  5967. } elseif (is_string($values)) {
  5968. if ('' === $values) {
  5969. return '';
  5970. }
  5971. if (null !== $charset = $env->getCharset()) {
  5972. if ('UTF-8' != $charset) {
  5973. $values = twig_convert_encoding($values, 'UTF-8', $charset);
  5974. }
  5975. // unicode version of str_split()
  5976. // split at all positions, but not after the start and not before the end
  5977. $values = preg_split('/(?<!^)(?!$)/u', $values);
  5978. if ('UTF-8' != $charset) {
  5979. foreach ($values as $i => $value) {
  5980. $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
  5981. }
  5982. }
  5983. } else {
  5984. return $values[mt_rand(0, strlen($values) - 1)];
  5985. }
  5986. }
  5987. if (!is_array($values)) {
  5988. return $values;
  5989. }
  5990. if (0 === count($values)) {
  5991. throw new Twig_Error_Runtime('The random function cannot pick from an empty array.');
  5992. }
  5993. return $values[array_rand($values, 1)];
  5994. }
  5995. /**
  5996. * Converts a date to the given format.
  5997. *
  5998. * <pre>
  5999. * {{ post.published_at|date("m/d/Y") }}
  6000. * </pre>
  6001. *
  6002. * @param Twig_Environment $env A Twig_Environment instance
  6003. * @param DateTime|DateInterval|string $date A date
  6004. * @param string $format A format
  6005. * @param DateTimeZone|string $timezone A timezone
  6006. *
  6007. * @return string The formatted date
  6008. */
  6009. function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
  6010. {
  6011. if (null === $format) {
  6012. $formats = $env->getExtension('core')->getDateFormat();
  6013. $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
  6014. }
  6015. if ($date instanceof DateInterval) {
  6016. return $date->format($format);
  6017. }
  6018. return twig_date_converter($env, $date, $timezone)->format($format);
  6019. }
  6020. /**
  6021. * Returns a new date object modified
  6022. *
  6023. * <pre>
  6024. * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
  6025. * </pre>
  6026. *
  6027. * @param Twig_Environment $env A Twig_Environment instance
  6028. * @param DateTime|string $date A date
  6029. * @param string $modifier A modifier string
  6030. *
  6031. * @return DateTime A new date object
  6032. */
  6033. function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
  6034. {
  6035. $date = twig_date_converter($env, $date, false);
  6036. $date->modify($modifier);
  6037. return $date;
  6038. }
  6039. /**
  6040. * Converts an input to a DateTime instance.
  6041. *
  6042. * <pre>
  6043. * {% if date(user.created_at) < date('+2days') %}
  6044. * {# do something #}
  6045. * {% endif %}
  6046. * </pre>
  6047. *
  6048. * @param Twig_Environment $env A Twig_Environment instance
  6049. * @param DateTime|string $date A date
  6050. * @param DateTimeZone|string $timezone A timezone
  6051. *
  6052. * @return DateTime A DateTime instance
  6053. */
  6054. function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
  6055. {
  6056. // determine the timezone
  6057. if (!$timezone) {
  6058. $defaultTimezone = $env->getExtension('core')->getTimezone();
  6059. } elseif (!$timezone instanceof DateTimeZone) {
  6060. $defaultTimezone = new DateTimeZone($timezone);
  6061. } else {
  6062. $defaultTimezone = $timezone;
  6063. }
  6064. if ($date instanceof DateTime) {
  6065. $date = clone $date;
  6066. if (false !== $timezone) {
  6067. $date->setTimezone($defaultTimezone);
  6068. }
  6069. return $date;
  6070. }
  6071. $asString = (string) $date;
  6072. if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
  6073. $date = new DateTime('@'.$date);
  6074. $date->setTimezone($defaultTimezone);
  6075. return $date;
  6076. }
  6077. return new DateTime($date, $defaultTimezone);
  6078. }
  6079. /**
  6080. * Number format filter.
  6081. *
  6082. * All of the formatting options can be left null, in that case the defaults will
  6083. * be used. Supplying any of the parameters will override the defaults set in the
  6084. * environment object.
  6085. *
  6086. * @param Twig_Environment $env A Twig_Environment instance
  6087. * @param mixed $number A float/int/string of the number to format
  6088. * @param integer $decimal The number of decimal points to display.
  6089. * @param string $decimalPoint The character(s) to use for the decimal point.
  6090. * @param string $thousandSep The character(s) to use for the thousands separator.
  6091. *
  6092. * @return string The formatted number
  6093. */
  6094. function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
  6095. {
  6096. $defaults = $env->getExtension('core')->getNumberFormat();
  6097. if (null === $decimal) {
  6098. $decimal = $defaults[0];
  6099. }
  6100. if (null === $decimalPoint) {
  6101. $decimalPoint = $defaults[1];
  6102. }
  6103. if (null === $thousandSep) {
  6104. $thousandSep = $defaults[2];
  6105. }
  6106. return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
  6107. }
  6108. /**
  6109. * URL encodes a string.
  6110. *
  6111. * @param string $url A URL
  6112. * @param bool $raw true to use rawurlencode() instead of urlencode
  6113. *
  6114. * @return string The URL encoded value
  6115. */
  6116. function twig_urlencode_filter($url, $raw = false)
  6117. {
  6118. if ($raw) {
  6119. return rawurlencode($url);
  6120. }
  6121. return urlencode($url);
  6122. }
  6123. if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  6124. /**
  6125. * JSON encodes a variable.
  6126. *
  6127. * @param mixed $value The value to encode.
  6128. * @param integer $options Not used on PHP 5.2.x
  6129. *
  6130. * @return mixed The JSON encoded value
  6131. */
  6132. function twig_jsonencode_filter($value, $options = 0)
  6133. {
  6134. if ($value instanceof Twig_Markup) {
  6135. $value = (string) $value;
  6136. } elseif (is_array($value)) {
  6137. array_walk_recursive($value, '_twig_markup2string');
  6138. }
  6139. return json_encode($value);
  6140. }
  6141. } else {
  6142. /**
  6143. * JSON encodes a variable.
  6144. *
  6145. * @param mixed $value The value to encode.
  6146. * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
  6147. *
  6148. * @return mixed The JSON encoded value
  6149. */
  6150. function twig_jsonencode_filter($value, $options = 0)
  6151. {
  6152. if ($value instanceof Twig_Markup) {
  6153. $value = (string) $value;
  6154. } elseif (is_array($value)) {
  6155. array_walk_recursive($value, '_twig_markup2string');
  6156. }
  6157. return json_encode($value, $options);
  6158. }
  6159. }
  6160. function _twig_markup2string(&$value)
  6161. {
  6162. if ($value instanceof Twig_Markup) {
  6163. $value = (string) $value;
  6164. }
  6165. }
  6166. /**
  6167. * Merges an array with another one.
  6168. *
  6169. * <pre>
  6170. * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
  6171. *
  6172. * {% set items = items|merge({ 'peugeot': 'car' }) %}
  6173. *
  6174. * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
  6175. * </pre>
  6176. *
  6177. * @param array $arr1 An array
  6178. * @param array $arr2 An array
  6179. *
  6180. * @return array The merged array
  6181. */
  6182. function twig_array_merge($arr1, $arr2)
  6183. {
  6184. if (!is_array($arr1) || !is_array($arr2)) {
  6185. throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.');
  6186. }
  6187. return array_merge($arr1, $arr2);
  6188. }
  6189. /**
  6190. * Slices a variable.
  6191. *
  6192. * @param Twig_Environment $env A Twig_Environment instance
  6193. * @param mixed $item A variable
  6194. * @param integer $start Start of the slice
  6195. * @param integer $length Size of the slice
  6196. * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array)
  6197. *
  6198. * @return mixed The sliced variable
  6199. */
  6200. function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
  6201. {
  6202. if ($item instanceof Traversable) {
  6203. $item = iterator_to_array($item, false);
  6204. }
  6205. if (is_array($item)) {
  6206. return array_slice($item, $start, $length, $preserveKeys);
  6207. }
  6208. $item = (string) $item;
  6209. if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
  6210. return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
  6211. }
  6212. return null === $length ? substr($item, $start) : substr($item, $start, $length);
  6213. }
  6214. /**
  6215. * Joins the values to a string.
  6216. *
  6217. * The separator between elements is an empty string per default, you can define it with the optional parameter.
  6218. *
  6219. * <pre>
  6220. * {{ [1, 2, 3]|join('|') }}
  6221. * {# returns 1|2|3 #}
  6222. *
  6223. * {{ [1, 2, 3]|join }}
  6224. * {# returns 123 #}
  6225. * </pre>
  6226. *
  6227. * @param array $value An array
  6228. * @param string $glue The separator
  6229. *
  6230. * @return string The concatenated string
  6231. */
  6232. function twig_join_filter($value, $glue = '')
  6233. {
  6234. if ($value instanceof Traversable) {
  6235. $value = iterator_to_array($value, false);
  6236. }
  6237. return implode($glue, (array) $value);
  6238. }
  6239. /**
  6240. * Splits the string into an array.
  6241. *
  6242. * <pre>
  6243. * {{ "one,two,three"|split(',') }}
  6244. * {# returns [one, two, three] #}
  6245. *
  6246. * {{ "one,two,three,four,five"|split(',', 3) }}
  6247. * {# returns [one, two, "three,four,five"] #}
  6248. *
  6249. * {{ "123"|split('') }}
  6250. * {# returns [1, 2, 3] #}
  6251. *
  6252. * {{ "aabbcc"|split('', 2) }}
  6253. * {# returns [aa, bb, cc] #}
  6254. * </pre>
  6255. *
  6256. * @param string $value A string
  6257. * @param string $delimiter The delimiter
  6258. * @param integer $limit The limit
  6259. *
  6260. * @return array The split string as an array
  6261. */
  6262. function twig_split_filter($value, $delimiter, $limit = null)
  6263. {
  6264. if (empty($delimiter)) {
  6265. return str_split($value, null === $limit ? 1 : $limit);
  6266. }
  6267. return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
  6268. }
  6269. // The '_default' filter is used internally to avoid using the ternary operator
  6270. // which costs a lot for big contexts (before PHP 5.4). So, on average,
  6271. // a function call is cheaper.
  6272. function _twig_default_filter($value, $default = '')
  6273. {
  6274. if (twig_test_empty($value)) {
  6275. return $default;
  6276. }
  6277. return $value;
  6278. }
  6279. /**
  6280. * Returns the keys for the given array.
  6281. *
  6282. * It is useful when you want to iterate over the keys of an array:
  6283. *
  6284. * <pre>
  6285. * {% for key in array|keys %}
  6286. * {# ... #}
  6287. * {% endfor %}
  6288. * </pre>
  6289. *
  6290. * @param array $array An array
  6291. *
  6292. * @return array The keys
  6293. */
  6294. function twig_get_array_keys_filter($array)
  6295. {
  6296. if (is_object($array) && $array instanceof Traversable) {
  6297. return array_keys(iterator_to_array($array));
  6298. }
  6299. if (!is_array($array)) {
  6300. return array();
  6301. }
  6302. return array_keys($array);
  6303. }
  6304. /**
  6305. * Reverses a variable.
  6306. *
  6307. * @param Twig_Environment $env A Twig_Environment instance
  6308. * @param array|Traversable|string $item An array, a Traversable instance, or a string
  6309. * @param Boolean $preserveKeys Whether to preserve key or not
  6310. *
  6311. * @return mixed The reversed input
  6312. */
  6313. function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false)
  6314. {
  6315. if (is_object($item) && $item instanceof Traversable) {
  6316. return array_reverse(iterator_to_array($item), $preserveKeys);
  6317. }
  6318. if (is_array($item)) {
  6319. return array_reverse($item, $preserveKeys);
  6320. }
  6321. if (null !== $charset = $env->getCharset()) {
  6322. $string = (string) $item;
  6323. if ('UTF-8' != $charset) {
  6324. $item = twig_convert_encoding($string, 'UTF-8', $charset);
  6325. }
  6326. preg_match_all('/./us', $item, $matches);
  6327. $string = implode('', array_reverse($matches[0]));
  6328. if ('UTF-8' != $charset) {
  6329. $string = twig_convert_encoding($string, $charset, 'UTF-8');
  6330. }
  6331. return $string;
  6332. }
  6333. return strrev((string) $item);
  6334. }
  6335. /**
  6336. * Sorts an array.
  6337. *
  6338. * @param array $array An array
  6339. */
  6340. function twig_sort_filter($array)
  6341. {
  6342. asort($array);
  6343. return $array;
  6344. }
  6345. /* used internally */
  6346. function twig_in_filter($value, $compare)
  6347. {
  6348. if (is_array($compare)) {
  6349. return in_array($value, $compare, is_object($value));
  6350. } elseif (is_string($compare)) {
  6351. if (!strlen($value)) {
  6352. return empty($compare);
  6353. }
  6354. return false !== strpos($compare, (string) $value);
  6355. } elseif ($compare instanceof Traversable) {
  6356. return in_array($value, iterator_to_array($compare, false), is_object($value));
  6357. }
  6358. return false;
  6359. }
  6360. /**
  6361. * Escapes a string.
  6362. *
  6363. * @param Twig_Environment $env A Twig_Environment instance
  6364. * @param string $string The value to be escaped
  6365. * @param string $strategy The escaping strategy
  6366. * @param string $charset The charset
  6367. * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
  6368. */
  6369. function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
  6370. {
  6371. if ($autoescape && is_object($string) && $string instanceof Twig_Markup) {
  6372. return $string;
  6373. }
  6374. if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
  6375. return $string;
  6376. }
  6377. if (null === $charset) {
  6378. $charset = $env->getCharset();
  6379. }
  6380. $string = (string) $string;
  6381. switch ($strategy) {
  6382. case 'js':
  6383. // escape all non-alphanumeric characters
  6384. // into their \xHH or \uHHHH representations
  6385. if ('UTF-8' != $charset) {
  6386. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  6387. }
  6388. if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
  6389. throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
  6390. }
  6391. $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
  6392. if ('UTF-8' != $charset) {
  6393. $string = twig_convert_encoding($string, $charset, 'UTF-8');
  6394. }
  6395. return $string;
  6396. case 'css':
  6397. if ('UTF-8' != $charset) {
  6398. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  6399. }
  6400. if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
  6401. throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
  6402. }
  6403. $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
  6404. if ('UTF-8' != $charset) {
  6405. $string = twig_convert_encoding($string, $charset, 'UTF-8');
  6406. }
  6407. return $string;
  6408. case 'html_attr':
  6409. if ('UTF-8' != $charset) {
  6410. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  6411. }
  6412. if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
  6413. throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
  6414. }
  6415. $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
  6416. if ('UTF-8' != $charset) {
  6417. $string = twig_convert_encoding($string, $charset, 'UTF-8');
  6418. }
  6419. return $string;
  6420. case 'html':
  6421. // see http://php.net/htmlspecialchars
  6422. // Using a static variable to avoid initializing the array
  6423. // each time the function is called. Moving the declaration on the
  6424. // top of the function slow downs other escaping strategies.
  6425. static $htmlspecialcharsCharsets = array(
  6426. 'iso-8859-1' => true, 'iso8859-1' => true,
  6427. 'iso-8859-15' => true, 'iso8859-15' => true,
  6428. 'utf-8' => true,
  6429. 'cp866' => true, 'ibm866' => true, '866' => true,
  6430. 'cp1251' => true, 'windows-1251' => true, 'win-1251' => true,
  6431. '1251' => true,
  6432. 'cp1252' => true, 'windows-1252' => true, '1252' => true,
  6433. 'koi8-r' => true, 'koi8-ru' => true, 'koi8r' => true,
  6434. 'big5' => true, '950' => true,
  6435. 'gb2312' => true, '936' => true,
  6436. 'big5-hkscs' => true,
  6437. 'shift_jis' => true, 'sjis' => true, '932' => true,
  6438. 'euc-jp' => true, 'eucjp' => true,
  6439. 'iso8859-5' => true, 'iso-8859-5' => true, 'macroman' => true,
  6440. );
  6441. if (isset($htmlspecialcharsCharsets[strtolower($charset)])) {
  6442. return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
  6443. }
  6444. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  6445. $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
  6446. return twig_convert_encoding($string, $charset, 'UTF-8');
  6447. case 'url':
  6448. if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  6449. return str_replace('%7E', '~', rawurlencode($string));
  6450. }
  6451. return rawurlencode($string);
  6452. default:
  6453. throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy));
  6454. }
  6455. }
  6456. /* used internally */
  6457. function twig_escape_filter_is_safe(Twig_Node $filterArgs)
  6458. {
  6459. foreach ($filterArgs as $arg) {
  6460. if ($arg instanceof Twig_Node_Expression_Constant) {
  6461. return array($arg->getAttribute('value'));
  6462. }
  6463. return array();
  6464. }
  6465. return array('html');
  6466. }
  6467. if (function_exists('mb_convert_encoding')) {
  6468. function twig_convert_encoding($string, $to, $from)
  6469. {
  6470. return mb_convert_encoding($string, $to, $from);
  6471. }
  6472. } elseif (function_exists('iconv')) {
  6473. function twig_convert_encoding($string, $to, $from)
  6474. {
  6475. return iconv($from, $to, $string);
  6476. }
  6477. } else {
  6478. function twig_convert_encoding($string, $to, $from)
  6479. {
  6480. throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
  6481. }
  6482. }
  6483. function _twig_escape_js_callback($matches)
  6484. {
  6485. $char = $matches[0];
  6486. // \xHH
  6487. if (!isset($char[1])) {
  6488. return '\\x'.strtoupper(substr('00'.bin2hex($char), -2));
  6489. }
  6490. // \uHHHH
  6491. $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
  6492. return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
  6493. }
  6494. function _twig_escape_css_callback($matches)
  6495. {
  6496. $char = $matches[0];
  6497. // \xHH
  6498. if (!isset($char[1])) {
  6499. $hex = ltrim(strtoupper(bin2hex($char)), '0');
  6500. if (0 === strlen($hex)) {
  6501. $hex = '0';
  6502. }
  6503. return '\\'.$hex.' ';
  6504. }
  6505. // \uHHHH
  6506. $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
  6507. return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
  6508. }
  6509. /**
  6510. * This function is adapted from code coming from Zend Framework.
  6511. *
  6512. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  6513. * @license http://framework.zend.com/license/new-bsd New BSD License
  6514. */
  6515. function _twig_escape_html_attr_callback($matches)
  6516. {
  6517. /*
  6518. * While HTML supports far more named entities, the lowest common denominator
  6519. * has become HTML5's XML Serialisation which is restricted to the those named
  6520. * entities that XML supports. Using HTML entities would result in this error:
  6521. * XML Parsing Error: undefined entity
  6522. */
  6523. static $entityMap = array(
  6524. 34 => 'quot', /* quotation mark */
  6525. 38 => 'amp', /* ampersand */
  6526. 60 => 'lt', /* less-than sign */
  6527. 62 => 'gt', /* greater-than sign */
  6528. );
  6529. $chr = $matches[0];
  6530. $ord = ord($chr);
  6531. /**
  6532. * The following replaces characters undefined in HTML with the
  6533. * hex entity for the Unicode replacement character.
  6534. */
  6535. if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
  6536. return '&#xFFFD;';
  6537. }
  6538. /**
  6539. * Check if the current character to escape has a name entity we should
  6540. * replace it with while grabbing the hex value of the character.
  6541. */
  6542. if (strlen($chr) == 1) {
  6543. $hex = strtoupper(substr('00'.bin2hex($chr), -2));
  6544. } else {
  6545. $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
  6546. $hex = strtoupper(substr('0000'.bin2hex($chr), -4));
  6547. }
  6548. $int = hexdec($hex);
  6549. if (array_key_exists($int, $entityMap)) {
  6550. return sprintf('&%s;', $entityMap[$int]);
  6551. }
  6552. /**
  6553. * Per OWASP recommendations, we'll use hex entities for any other
  6554. * characters where a named entity does not exist.
  6555. */
  6556. return sprintf('&#x%s;', $hex);
  6557. }
  6558. // add multibyte extensions if possible
  6559. if (function_exists('mb_get_info')) {
  6560. /**
  6561. * Returns the length of a variable.
  6562. *
  6563. * @param Twig_Environment $env A Twig_Environment instance
  6564. * @param mixed $thing A variable
  6565. *
  6566. * @return integer The length of the value
  6567. */
  6568. function twig_length_filter(Twig_Environment $env, $thing)
  6569. {
  6570. return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
  6571. }
  6572. /**
  6573. * Converts a string to uppercase.
  6574. *
  6575. * @param Twig_Environment $env A Twig_Environment instance
  6576. * @param string $string A string
  6577. *
  6578. * @return string The uppercased string
  6579. */
  6580. function twig_upper_filter(Twig_Environment $env, $string)
  6581. {
  6582. if (null !== ($charset = $env->getCharset())) {
  6583. return mb_strtoupper($string, $charset);
  6584. }
  6585. return strtoupper($string);
  6586. }
  6587. /**
  6588. * Converts a string to lowercase.
  6589. *
  6590. * @param Twig_Environment $env A Twig_Environment instance
  6591. * @param string $string A string
  6592. *
  6593. * @return string The lowercased string
  6594. */
  6595. function twig_lower_filter(Twig_Environment $env, $string)
  6596. {
  6597. if (null !== ($charset = $env->getCharset())) {
  6598. return mb_strtolower($string, $charset);
  6599. }
  6600. return strtolower($string);
  6601. }
  6602. /**
  6603. * Returns a titlecased string.
  6604. *
  6605. * @param Twig_Environment $env A Twig_Environment instance
  6606. * @param string $string A string
  6607. *
  6608. * @return string The titlecased string
  6609. */
  6610. function twig_title_string_filter(Twig_Environment $env, $string)
  6611. {
  6612. if (null !== ($charset = $env->getCharset())) {
  6613. return mb_convert_case($string, MB_CASE_TITLE, $charset);
  6614. }
  6615. return ucwords(strtolower($string));
  6616. }
  6617. /**
  6618. * Returns a capitalized string.
  6619. *
  6620. * @param Twig_Environment $env A Twig_Environment instance
  6621. * @param string $string A string
  6622. *
  6623. * @return string The capitalized string
  6624. */
  6625. function twig_capitalize_string_filter(Twig_Environment $env, $string)
  6626. {
  6627. if (null !== ($charset = $env->getCharset())) {
  6628. return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).
  6629. mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
  6630. }
  6631. return ucfirst(strtolower($string));
  6632. }
  6633. }
  6634. // and byte fallback
  6635. else {
  6636. /**
  6637. * Returns the length of a variable.
  6638. *
  6639. * @param Twig_Environment $env A Twig_Environment instance
  6640. * @param mixed $thing A variable
  6641. *
  6642. * @return integer The length of the value
  6643. */
  6644. function twig_length_filter(Twig_Environment $env, $thing)
  6645. {
  6646. return is_scalar($thing) ? strlen($thing) : count($thing);
  6647. }
  6648. /**
  6649. * Returns a titlecased string.
  6650. *
  6651. * @param Twig_Environment $env A Twig_Environment instance
  6652. * @param string $string A string
  6653. *
  6654. * @return string The titlecased string
  6655. */
  6656. function twig_title_string_filter(Twig_Environment $env, $string)
  6657. {
  6658. return ucwords(strtolower($string));
  6659. }
  6660. /**
  6661. * Returns a capitalized string.
  6662. *
  6663. * @param Twig_Environment $env A Twig_Environment instance
  6664. * @param string $string A string
  6665. *
  6666. * @return string The capitalized string
  6667. */
  6668. function twig_capitalize_string_filter(Twig_Environment $env, $string)
  6669. {
  6670. return ucfirst(strtolower($string));
  6671. }
  6672. }
  6673. /* used internally */
  6674. function twig_ensure_traversable($seq)
  6675. {
  6676. if ($seq instanceof Traversable || is_array($seq)) {
  6677. return $seq;
  6678. }
  6679. return array();
  6680. }
  6681. /**
  6682. * Checks if a variable is empty.
  6683. *
  6684. * <pre>
  6685. * {# evaluates to true if the foo variable is null, false, or the empty string #}
  6686. * {% if foo is empty %}
  6687. * {# ... #}
  6688. * {% endif %}
  6689. * </pre>
  6690. *
  6691. * @param mixed $value A variable
  6692. *
  6693. * @return Boolean true if the value is empty, false otherwise
  6694. */
  6695. function twig_test_empty($value)
  6696. {
  6697. if ($value instanceof Countable) {
  6698. return 0 == count($value);
  6699. }
  6700. return '' === $value || false === $value || null === $value || array() === $value;
  6701. }
  6702. /**
  6703. * Checks if a variable is traversable.
  6704. *
  6705. * <pre>
  6706. * {# evaluates to true if the foo variable is an array or a traversable object #}
  6707. * {% if foo is traversable %}
  6708. * {# ... #}
  6709. * {% endif %}
  6710. * </pre>
  6711. *
  6712. * @param mixed $value A variable
  6713. *
  6714. * @return Boolean true if the value is traversable
  6715. */
  6716. function twig_test_iterable($value)
  6717. {
  6718. return $value instanceof Traversable || is_array($value);
  6719. }
  6720. /**
  6721. * Renders a template.
  6722. *
  6723. * @param string template The template to render
  6724. * @param array variables The variables to pass to the template
  6725. * @param Boolean with_context Whether to pass the current context variables or not
  6726. * @param Boolean ignore_missing Whether to ignore missing templates or not
  6727. * @param Boolean sandboxed Whether to sandbox the template or not
  6728. *
  6729. * @return string The rendered template
  6730. */
  6731. function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
  6732. {
  6733. if ($withContext) {
  6734. $variables = array_merge($context, $variables);
  6735. }
  6736. if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) {
  6737. $sandbox = $env->getExtension('sandbox');
  6738. if (!$alreadySandboxed = $sandbox->isSandboxed()) {
  6739. $sandbox->enableSandbox();
  6740. }
  6741. }
  6742. try {
  6743. return $env->resolveTemplate($template)->display($variables);
  6744. } catch (Twig_Error_Loader $e) {
  6745. if (!$ignoreMissing) {
  6746. throw $e;
  6747. }
  6748. }
  6749. if ($isSandboxed && !$alreadySandboxed) {
  6750. $sandbox->disableSandbox();
  6751. }
  6752. }
  6753. /**
  6754. * Provides the ability to get constants from instances as well as class/global constants.
  6755. *
  6756. * @param string $constant The name of the constant
  6757. * @param null|object $object The object to get the constant from
  6758. *
  6759. * @return string
  6760. */
  6761. function twig_constant($constant, $object = null)
  6762. {
  6763. if (!$object) {
  6764. return constant($constant);
  6765. }
  6766. $class = get_class($object);
  6767. return constant($class.'::'.$constant);
  6768. }
  6769. }
  6770. namespace
  6771. {
  6772. /*
  6773. * This file is part of Twig.
  6774. *
  6775. * (c) 2009 Fabien Potencier
  6776. *
  6777. * For the full copyright and license information, please view the LICENSE
  6778. * file that was distributed with this source code.
  6779. */
  6780. class Twig_Extension_Escaper extends Twig_Extension
  6781. {
  6782. protected $defaultStrategy;
  6783. public function __construct($defaultStrategy = 'html')
  6784. {
  6785. $this->setDefaultStrategy($defaultStrategy);
  6786. }
  6787. /**
  6788. * Returns the token parser instances to add to the existing list.
  6789. *
  6790. * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
  6791. */
  6792. public function getTokenParsers()
  6793. {
  6794. return array(new Twig_TokenParser_AutoEscape());
  6795. }
  6796. /**
  6797. * Returns the node visitor instances to add to the existing list.
  6798. *
  6799. * @return array An array of Twig_NodeVisitorInterface instances
  6800. */
  6801. public function getNodeVisitors()
  6802. {
  6803. return array(new Twig_NodeVisitor_Escaper());
  6804. }
  6805. /**
  6806. * Returns a list of filters to add to the existing list.
  6807. *
  6808. * @return array An array of filters
  6809. */
  6810. public function getFilters()
  6811. {
  6812. return array(
  6813. new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
  6814. );
  6815. }
  6816. /**
  6817. * Sets the default strategy to use when not defined by the user.
  6818. *
  6819. * The strategy can be a valid PHP callback that takes the template
  6820. * "filename" as an argument and returns the strategy to use.
  6821. *
  6822. * @param mixed $defaultStrategy An escaping strategy
  6823. */
  6824. public function setDefaultStrategy($defaultStrategy)
  6825. {
  6826. // for BC
  6827. if (true === $defaultStrategy) {
  6828. $defaultStrategy = 'html';
  6829. }
  6830. $this->defaultStrategy = $defaultStrategy;
  6831. }
  6832. /**
  6833. * Gets the default strategy to use when not defined by the user.
  6834. *
  6835. * @param string $filename The template "filename"
  6836. *
  6837. * @return string The default strategy to use for the template
  6838. */
  6839. public function getDefaultStrategy($filename)
  6840. {
  6841. // disable string callables to avoid calling a function named html or js,
  6842. // or any other upcoming escaping strategy
  6843. if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) {
  6844. return call_user_func($this->defaultStrategy, $filename);
  6845. }
  6846. return $this->defaultStrategy;
  6847. }
  6848. /**
  6849. * Returns the name of the extension.
  6850. *
  6851. * @return string The extension name
  6852. */
  6853. public function getName()
  6854. {
  6855. return 'escaper';
  6856. }
  6857. }
  6858. /**
  6859. * Marks a variable as being safe.
  6860. *
  6861. * @param string $string A PHP variable
  6862. */
  6863. function twig_raw_filter($string)
  6864. {
  6865. return $string;
  6866. }
  6867. }
  6868. namespace
  6869. {
  6870. /*
  6871. * This file is part of Twig.
  6872. *
  6873. * (c) 2010 Fabien Potencier
  6874. *
  6875. * For the full copyright and license information, please view the LICENSE
  6876. * file that was distributed with this source code.
  6877. */
  6878. class Twig_Extension_Optimizer extends Twig_Extension
  6879. {
  6880. protected $optimizers;
  6881. public function __construct($optimizers = -1)
  6882. {
  6883. $this->optimizers = $optimizers;
  6884. }
  6885. /**
  6886. * {@inheritdoc}
  6887. */
  6888. public function getNodeVisitors()
  6889. {
  6890. return array(new Twig_NodeVisitor_Optimizer($this->optimizers));
  6891. }
  6892. /**
  6893. * {@inheritdoc}
  6894. */
  6895. public function getName()
  6896. {
  6897. return 'optimizer';
  6898. }
  6899. }
  6900. }
  6901. namespace
  6902. {
  6903. /*
  6904. * This file is part of Twig.
  6905. *
  6906. * (c) 2009 Fabien Potencier
  6907. *
  6908. * For the full copyright and license information, please view the LICENSE
  6909. * file that was distributed with this source code.
  6910. */
  6911. /**
  6912. * Interface all loaders must implement.
  6913. *
  6914. * @package twig
  6915. * @author Fabien Potencier <fabien@symfony.com>
  6916. */
  6917. interface Twig_LoaderInterface
  6918. {
  6919. /**
  6920. * Gets the source code of a template, given its name.
  6921. *
  6922. * @param string $name The name of the template to load
  6923. *
  6924. * @return string The template source code
  6925. *
  6926. * @throws Twig_Error_Loader When $name is not found
  6927. */
  6928. public function getSource($name);
  6929. /**
  6930. * Gets the cache key to use for the cache for a given template name.
  6931. *
  6932. * @param string $name The name of the template to load
  6933. *
  6934. * @return string The cache key
  6935. *
  6936. * @throws Twig_Error_Loader When $name is not found
  6937. */
  6938. public function getCacheKey($name);
  6939. /**
  6940. * Returns true if the template is still fresh.
  6941. *
  6942. * @param string $name The template name
  6943. * @param timestamp $time The last modification time of the cached template
  6944. *
  6945. * @return Boolean true if the template is fresh, false otherwise
  6946. *
  6947. * @throws Twig_Error_Loader When $name is not found
  6948. */
  6949. public function isFresh($name, $time);
  6950. }
  6951. }
  6952. namespace
  6953. {
  6954. /*
  6955. * This file is part of Twig.
  6956. *
  6957. * (c) 2010 Fabien Potencier
  6958. *
  6959. * For the full copyright and license information, please view the LICENSE
  6960. * file that was distributed with this source code.
  6961. */
  6962. /**
  6963. * Marks a content as safe.
  6964. *
  6965. * @package twig
  6966. * @author Fabien Potencier <fabien@symfony.com>
  6967. */
  6968. class Twig_Markup implements Countable
  6969. {
  6970. protected $content;
  6971. protected $charset;
  6972. public function __construct($content, $charset)
  6973. {
  6974. $this->content = (string) $content;
  6975. $this->charset = $charset;
  6976. }
  6977. public function __toString()
  6978. {
  6979. return $this->content;
  6980. }
  6981. public function count()
  6982. {
  6983. return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
  6984. }
  6985. }
  6986. }
  6987. namespace
  6988. {
  6989. /*
  6990. * This file is part of Twig.
  6991. *
  6992. * (c) 2009 Fabien Potencier
  6993. *
  6994. * For the full copyright and license information, please view the LICENSE
  6995. * file that was distributed with this source code.
  6996. */
  6997. /**
  6998. * Interface implemented by all compiled templates.
  6999. *
  7000. * @package twig
  7001. * @author Fabien Potencier <fabien@symfony.com>
  7002. * @deprecated since 1.12 (to be removed in 2.0)
  7003. */
  7004. interface Twig_TemplateInterface
  7005. {
  7006. const ANY_CALL = 'any';
  7007. const ARRAY_CALL = 'array';
  7008. const METHOD_CALL = 'method';
  7009. /**
  7010. * Renders the template with the given context and returns it as string.
  7011. *
  7012. * @param array $context An array of parameters to pass to the template
  7013. *
  7014. * @return string The rendered template
  7015. */
  7016. public function render(array $context);
  7017. /**
  7018. * Displays the template with the given context.
  7019. *
  7020. * @param array $context An array of parameters to pass to the template
  7021. * @param array $blocks An array of blocks to pass to the template
  7022. */
  7023. public function display(array $context, array $blocks = array());
  7024. /**
  7025. * Returns the bound environment for this template.
  7026. *
  7027. * @return Twig_Environment The current environment
  7028. */
  7029. public function getEnvironment();
  7030. }
  7031. }
  7032. namespace
  7033. {
  7034. /*
  7035. * This file is part of Twig.
  7036. *
  7037. * (c) 2009 Fabien Potencier
  7038. * (c) 2009 Armin Ronacher
  7039. *
  7040. * For the full copyright and license information, please view the LICENSE
  7041. * file that was distributed with this source code.
  7042. */
  7043. /**
  7044. * Default base class for compiled templates.
  7045. *
  7046. * @package twig
  7047. * @author Fabien Potencier <fabien@symfony.com>
  7048. */
  7049. abstract class Twig_Template implements Twig_TemplateInterface
  7050. {
  7051. protected static $cache = array();
  7052. protected $parent;
  7053. protected $parents;
  7054. protected $env;
  7055. protected $blocks;
  7056. protected $traits;
  7057. /**
  7058. * Constructor.
  7059. *
  7060. * @param Twig_Environment $env A Twig_Environment instance
  7061. */
  7062. public function __construct(Twig_Environment $env)
  7063. {
  7064. $this->env = $env;
  7065. $this->blocks = array();
  7066. $this->traits = array();
  7067. }
  7068. /**
  7069. * Returns the template name.
  7070. *
  7071. * @return string The template name
  7072. */
  7073. abstract public function getTemplateName();
  7074. /**
  7075. * {@inheritdoc}
  7076. */
  7077. public function getEnvironment()
  7078. {
  7079. return $this->env;
  7080. }
  7081. /**
  7082. * Returns the parent template.
  7083. *
  7084. * This method is for internal use only and should never be called
  7085. * directly.
  7086. *
  7087. * @return Twig_TemplateInterface|false The parent template or false if there is no parent
  7088. */
  7089. public function getParent(array $context)
  7090. {
  7091. if (null !== $this->parent) {
  7092. return $this->parent;
  7093. }
  7094. $parent = $this->doGetParent($context);
  7095. if (false === $parent) {
  7096. return false;
  7097. } elseif ($parent instanceof Twig_Template) {
  7098. $name = $parent->getTemplateName();
  7099. $this->parents[$name] = $parent;
  7100. $parent = $name;
  7101. } elseif (!isset($this->parents[$parent])) {
  7102. $this->parents[$parent] = $this->env->loadTemplate($parent);
  7103. }
  7104. return $this->parents[$parent];
  7105. }
  7106. protected function doGetParent(array $context)
  7107. {
  7108. return false;
  7109. }
  7110. public function isTraitable()
  7111. {
  7112. return true;
  7113. }
  7114. /**
  7115. * Displays a parent block.
  7116. *
  7117. * This method is for internal use only and should never be called
  7118. * directly.
  7119. *
  7120. * @param string $name The block name to display from the parent
  7121. * @param array $context The context
  7122. * @param array $blocks The current set of blocks
  7123. */
  7124. public function displayParentBlock($name, array $context, array $blocks = array())
  7125. {
  7126. $name = (string) $name;
  7127. if (isset($this->traits[$name])) {
  7128. $this->traits[$name][0]->displayBlock($name, $context, $blocks);
  7129. } elseif (false !== $parent = $this->getParent($context)) {
  7130. $parent->displayBlock($name, $context, $blocks);
  7131. } else {
  7132. throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
  7133. }
  7134. }
  7135. /**
  7136. * Displays a block.
  7137. *
  7138. * This method is for internal use only and should never be called
  7139. * directly.
  7140. *
  7141. * @param string $name The block name to display
  7142. * @param array $context The context
  7143. * @param array $blocks The current set of blocks
  7144. */
  7145. public function displayBlock($name, array $context, array $blocks = array())
  7146. {
  7147. $name = (string) $name;
  7148. if (isset($blocks[$name])) {
  7149. $b = $blocks;
  7150. unset($b[$name]);
  7151. call_user_func($blocks[$name], $context, $b);
  7152. } elseif (isset($this->blocks[$name])) {
  7153. call_user_func($this->blocks[$name], $context, $blocks);
  7154. } elseif (false !== $parent = $this->getParent($context)) {
  7155. $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
  7156. }
  7157. }
  7158. /**
  7159. * Renders a parent block.
  7160. *
  7161. * This method is for internal use only and should never be called
  7162. * directly.
  7163. *
  7164. * @param string $name The block name to render from the parent
  7165. * @param array $context The context
  7166. * @param array $blocks The current set of blocks
  7167. *
  7168. * @return string The rendered block
  7169. */
  7170. public function renderParentBlock($name, array $context, array $blocks = array())
  7171. {
  7172. ob_start();
  7173. $this->displayParentBlock($name, $context, $blocks);
  7174. return ob_get_clean();
  7175. }
  7176. /**
  7177. * Renders a block.
  7178. *
  7179. * This method is for internal use only and should never be called
  7180. * directly.
  7181. *
  7182. * @param string $name The block name to render
  7183. * @param array $context The context
  7184. * @param array $blocks The current set of blocks
  7185. *
  7186. * @return string The rendered block
  7187. */
  7188. public function renderBlock($name, array $context, array $blocks = array())
  7189. {
  7190. ob_start();
  7191. $this->displayBlock($name, $context, $blocks);
  7192. return ob_get_clean();
  7193. }
  7194. /**
  7195. * Returns whether a block exists or not.
  7196. *
  7197. * This method is for internal use only and should never be called
  7198. * directly.
  7199. *
  7200. * This method does only return blocks defined in the current template
  7201. * or defined in "used" traits.
  7202. *
  7203. * It does not return blocks from parent templates as the parent
  7204. * template name can be dynamic, which is only known based on the
  7205. * current context.
  7206. *
  7207. * @param string $name The block name
  7208. *
  7209. * @return Boolean true if the block exists, false otherwise
  7210. */
  7211. public function hasBlock($name)
  7212. {
  7213. return isset($this->blocks[(string) $name]);
  7214. }
  7215. /**
  7216. * Returns all block names.
  7217. *
  7218. * This method is for internal use only and should never be called
  7219. * directly.
  7220. *
  7221. * @return array An array of block names
  7222. *
  7223. * @see hasBlock
  7224. */
  7225. public function getBlockNames()
  7226. {
  7227. return array_keys($this->blocks);
  7228. }
  7229. /**
  7230. * Returns all blocks.
  7231. *
  7232. * This method is for internal use only and should never be called
  7233. * directly.
  7234. *
  7235. * @return array An array of blocks
  7236. *
  7237. * @see hasBlock
  7238. */
  7239. public function getBlocks()
  7240. {
  7241. return $this->blocks;
  7242. }
  7243. /**
  7244. * {@inheritdoc}
  7245. */
  7246. public function display(array $context, array $blocks = array())
  7247. {
  7248. $this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks);
  7249. }
  7250. /**
  7251. * {@inheritdoc}
  7252. */
  7253. public function render(array $context)
  7254. {
  7255. $level = ob_get_level();
  7256. ob_start();
  7257. try {
  7258. $this->display($context);
  7259. } catch (Exception $e) {
  7260. while (ob_get_level() > $level) {
  7261. ob_end_clean();
  7262. }
  7263. throw $e;
  7264. }
  7265. return ob_get_clean();
  7266. }
  7267. protected function displayWithErrorHandling(array $context, array $blocks = array())
  7268. {
  7269. try {
  7270. $this->doDisplay($context, $blocks);
  7271. } catch (Twig_Error $e) {
  7272. if (!$e->getTemplateFile()) {
  7273. $e->setTemplateFile($this->getTemplateName());
  7274. }
  7275. // this is mostly useful for Twig_Error_Loader exceptions
  7276. // see Twig_Error_Loader
  7277. if (false === $e->getTemplateLine()) {
  7278. $e->setTemplateLine(-1);
  7279. $e->guess();
  7280. }
  7281. throw $e;
  7282. } catch (Exception $e) {
  7283. throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
  7284. }
  7285. }
  7286. /**
  7287. * Auto-generated method to display the template with the given context.
  7288. *
  7289. * @param array $context An array of parameters to pass to the template
  7290. * @param array $blocks An array of blocks to pass to the template
  7291. */
  7292. abstract protected function doDisplay(array $context, array $blocks = array());
  7293. /**
  7294. * Returns a variable from the context.
  7295. *
  7296. * This method is for internal use only and should never be called
  7297. * directly.
  7298. *
  7299. * This method should not be overridden in a sub-class as this is an
  7300. * implementation detail that has been introduced to optimize variable
  7301. * access for versions of PHP before 5.4. This is not a way to override
  7302. * the way to get a variable value.
  7303. *
  7304. * @param array $context The context
  7305. * @param string $item The variable to return from the context
  7306. * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
  7307. *
  7308. * @return The content of the context variable
  7309. *
  7310. * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
  7311. */
  7312. final protected function getContext($context, $item, $ignoreStrictCheck = false)
  7313. {
  7314. if (!array_key_exists($item, $context)) {
  7315. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  7316. return null;
  7317. }
  7318. throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
  7319. }
  7320. return $context[$item];
  7321. }
  7322. /**
  7323. * Returns the attribute value for a given array/object.
  7324. *
  7325. * @param mixed $object The object or array from where to get the item
  7326. * @param mixed $item The item to get from the array or object
  7327. * @param array $arguments An array of arguments to pass if the item is an object method
  7328. * @param string $type The type of attribute (@see Twig_TemplateInterface)
  7329. * @param Boolean $isDefinedTest Whether this is only a defined check
  7330. * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
  7331. *
  7332. * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
  7333. *
  7334. * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
  7335. */
  7336. protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
  7337. {
  7338. $item = ctype_digit((string) $item) ? (int) $item : (string) $item;
  7339. // array
  7340. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  7341. if ((is_array($object) && array_key_exists($item, $object))
  7342. || ($object instanceof ArrayAccess && isset($object[$item]))
  7343. ) {
  7344. if ($isDefinedTest) {
  7345. return true;
  7346. }
  7347. return $object[$item];
  7348. }
  7349. if (Twig_TemplateInterface::ARRAY_CALL === $type) {
  7350. if ($isDefinedTest) {
  7351. return false;
  7352. }
  7353. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  7354. return null;
  7355. }
  7356. if (is_object($object)) {
  7357. throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
  7358. } elseif (is_array($object)) {
  7359. throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))), -1, $this->getTemplateName());
  7360. } else {
  7361. throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)), -1, $this->getTemplateName());
  7362. }
  7363. }
  7364. }
  7365. if (!is_object($object)) {
  7366. if ($isDefinedTest) {
  7367. return false;
  7368. }
  7369. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  7370. return null;
  7371. }
  7372. throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object), -1, $this->getTemplateName());
  7373. }
  7374. $class = get_class($object);
  7375. // object property
  7376. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  7377. if (isset($object->$item) || array_key_exists($item, $object)) {
  7378. if ($isDefinedTest) {
  7379. return true;
  7380. }
  7381. if ($this->env->hasExtension('sandbox')) {
  7382. $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
  7383. }
  7384. return $object->$item;
  7385. }
  7386. }
  7387. // object method
  7388. if (!isset(self::$cache[$class]['methods'])) {
  7389. self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
  7390. }
  7391. $lcItem = strtolower($item);
  7392. if (isset(self::$cache[$class]['methods'][$lcItem])) {
  7393. $method = $item;
  7394. } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
  7395. $method = 'get'.$item;
  7396. } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
  7397. $method = 'is'.$item;
  7398. } elseif (isset(self::$cache[$class]['methods']['__call'])) {
  7399. $method = $item;
  7400. } else {
  7401. if ($isDefinedTest) {
  7402. return false;
  7403. }
  7404. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  7405. return null;
  7406. }
  7407. throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
  7408. }
  7409. if ($isDefinedTest) {
  7410. return true;
  7411. }
  7412. if ($this->env->hasExtension('sandbox')) {
  7413. $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
  7414. }
  7415. $ret = call_user_func_array(array($object, $method), $arguments);
  7416. // useful when calling a template method from a template
  7417. // this is not supported but unfortunately heavily used in the Symfony profiler
  7418. if ($object instanceof Twig_TemplateInterface) {
  7419. return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
  7420. }
  7421. return $ret;
  7422. }
  7423. /**
  7424. * This method is only useful when testing Twig. Do not use it.
  7425. */
  7426. public static function clearCache()
  7427. {
  7428. self::$cache = array();
  7429. }
  7430. }
  7431. }
  7432. namespace Monolog\Formatter
  7433. {
  7434. interface FormatterInterface
  7435. {
  7436. public function format(array $record);
  7437. public function formatBatch(array $records);
  7438. }
  7439. }
  7440. namespace Monolog\Formatter
  7441. {
  7442. class NormalizerFormatter implements FormatterInterface
  7443. {
  7444. const SIMPLE_DATE = "Y-m-d H:i:s";
  7445. protected $dateFormat;
  7446. public function __construct($dateFormat = null)
  7447. {
  7448. $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE;
  7449. }
  7450. public function format(array $record)
  7451. {
  7452. return $this->normalize($record);
  7453. }
  7454. public function formatBatch(array $records)
  7455. {
  7456. foreach ($records as $key => $record) {
  7457. $records[$key] = $this->format($record);
  7458. }
  7459. return $records;
  7460. }
  7461. protected function normalize($data)
  7462. {
  7463. if (null === $data || is_scalar($data)) {
  7464. return $data;
  7465. }
  7466. if (is_array($data) || $data instanceof \Traversable) {
  7467. $normalized = array();
  7468. foreach ($data as $key => $value) {
  7469. $normalized[$key] = $this->normalize($value);
  7470. }
  7471. return $normalized;
  7472. }
  7473. if ($data instanceof \DateTime) {
  7474. return $data->format($this->dateFormat);
  7475. }
  7476. if (is_object($data)) {
  7477. return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data));
  7478. }
  7479. if (is_resource($data)) {
  7480. return '[resource]';
  7481. }
  7482. return '[unknown('.gettype($data).')]';
  7483. }
  7484. protected function toJson($data)
  7485. {
  7486. if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
  7487. return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  7488. }
  7489. return json_encode($data);
  7490. }
  7491. }
  7492. }
  7493. namespace Monolog\Formatter
  7494. {
  7495. class LineFormatter extends NormalizerFormatter
  7496. {
  7497. const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
  7498. protected $format;
  7499. public function __construct($format = null, $dateFormat = null)
  7500. {
  7501. $this->format = $format ?: static::SIMPLE_FORMAT;
  7502. parent::__construct($dateFormat);
  7503. }
  7504. public function format(array $record)
  7505. {
  7506. $vars = parent::format($record);
  7507. $output = $this->format;
  7508. foreach ($vars['extra'] as $var => $val) {
  7509. if (false !== strpos($output, '%extra.'.$var.'%')) {
  7510. $output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output);
  7511. unset($vars['extra'][$var]);
  7512. }
  7513. }
  7514. foreach ($vars as $var => $val) {
  7515. $output = str_replace('%'.$var.'%', $this->convertToString($val), $output);
  7516. }
  7517. return $output;
  7518. }
  7519. public function formatBatch(array $records)
  7520. {
  7521. $message = '';
  7522. foreach ($records as $record) {
  7523. $message .= $this->format($record);
  7524. }
  7525. return $message;
  7526. }
  7527. protected function normalize($data)
  7528. {
  7529. if (is_bool($data) || is_null($data)) {
  7530. return var_export($data, true);
  7531. }
  7532. return parent::normalize($data);
  7533. }
  7534. protected function convertToString($data)
  7535. {
  7536. if (null === $data || is_scalar($data)) {
  7537. return (string) $data;
  7538. }
  7539. if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
  7540. return json_encode($this->normalize($data), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  7541. }
  7542. return stripslashes(json_encode($this->normalize($data)));
  7543. }
  7544. }
  7545. }
  7546. namespace Monolog\Handler
  7547. {
  7548. use Monolog\Formatter\FormatterInterface;
  7549. interface HandlerInterface
  7550. {
  7551. public function isHandling(array $record);
  7552. public function handle(array $record);
  7553. public function handleBatch(array $records);
  7554. public function pushProcessor($callback);
  7555. public function popProcessor();
  7556. public function setFormatter(FormatterInterface $formatter);
  7557. public function getFormatter();
  7558. }
  7559. }
  7560. namespace Monolog\Handler
  7561. {
  7562. use Monolog\Logger;
  7563. use Monolog\Formatter\FormatterInterface;
  7564. use Monolog\Formatter\LineFormatter;
  7565. abstract class AbstractHandler implements HandlerInterface
  7566. {
  7567. protected $level = Logger::DEBUG;
  7568. protected $bubble = false;
  7569. protected $formatter;
  7570. protected $processors = array();
  7571. public function __construct($level = Logger::DEBUG, $bubble = true)
  7572. {
  7573. $this->level = $level;
  7574. $this->bubble = $bubble;
  7575. }
  7576. public function isHandling(array $record)
  7577. {
  7578. return $record['level'] >= $this->level;
  7579. }
  7580. public function handleBatch(array $records)
  7581. {
  7582. foreach ($records as $record) {
  7583. $this->handle($record);
  7584. }
  7585. }
  7586. public function close()
  7587. {
  7588. }
  7589. public function pushProcessor($callback)
  7590. {
  7591. if (!is_callable($callback)) {
  7592. throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
  7593. }
  7594. array_unshift($this->processors, $callback);
  7595. }
  7596. public function popProcessor()
  7597. {
  7598. if (!$this->processors) {
  7599. throw new \LogicException('You tried to pop from an empty processor stack.');
  7600. }
  7601. return array_shift($this->processors);
  7602. }
  7603. public function setFormatter(FormatterInterface $formatter)
  7604. {
  7605. $this->formatter = $formatter;
  7606. }
  7607. public function getFormatter()
  7608. {
  7609. if (!$this->formatter) {
  7610. $this->formatter = $this->getDefaultFormatter();
  7611. }
  7612. return $this->formatter;
  7613. }
  7614. public function setLevel($level)
  7615. {
  7616. $this->level = $level;
  7617. }
  7618. public function getLevel()
  7619. {
  7620. return $this->level;
  7621. }
  7622. public function setBubble($bubble)
  7623. {
  7624. $this->bubble = $bubble;
  7625. }
  7626. public function getBubble()
  7627. {
  7628. return $this->bubble;
  7629. }
  7630. public function __destruct()
  7631. {
  7632. try {
  7633. $this->close();
  7634. } catch (\Exception $e) {
  7635. }
  7636. }
  7637. protected function getDefaultFormatter()
  7638. {
  7639. return new LineFormatter();
  7640. }
  7641. }
  7642. }
  7643. namespace Monolog\Handler
  7644. {
  7645. abstract class AbstractProcessingHandler extends AbstractHandler
  7646. {
  7647. public function handle(array $record)
  7648. {
  7649. if ($record['level'] < $this->level) {
  7650. return false;
  7651. }
  7652. $record = $this->processRecord($record);
  7653. $record['formatted'] = $this->getFormatter()->format($record);
  7654. $this->write($record);
  7655. return false === $this->bubble;
  7656. }
  7657. abstract protected function write(array $record);
  7658. protected function processRecord(array $record)
  7659. {
  7660. if ($this->processors) {
  7661. foreach ($this->processors as $processor) {
  7662. $record = call_user_func($processor, $record);
  7663. }
  7664. }
  7665. return $record;
  7666. }
  7667. }
  7668. }
  7669. namespace Monolog\Handler
  7670. {
  7671. use Monolog\Logger;
  7672. class StreamHandler extends AbstractProcessingHandler
  7673. {
  7674. protected $stream;
  7675. protected $url;
  7676. public function __construct($stream, $level = Logger::DEBUG, $bubble = true)
  7677. {
  7678. parent::__construct($level, $bubble);
  7679. if (is_resource($stream)) {
  7680. $this->stream = $stream;
  7681. } else {
  7682. $this->url = $stream;
  7683. }
  7684. }
  7685. public function close()
  7686. {
  7687. if (is_resource($this->stream)) {
  7688. fclose($this->stream);
  7689. }
  7690. $this->stream = null;
  7691. }
  7692. protected function write(array $record)
  7693. {
  7694. if (null === $this->stream) {
  7695. if (!$this->url) {
  7696. throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
  7697. }
  7698. $errorMessage = null;
  7699. set_error_handler(function ($code, $msg) use (&$errorMessage) {
  7700. $errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg);
  7701. });
  7702. $this->stream = fopen($this->url, 'a');
  7703. restore_error_handler();
  7704. if (!is_resource($this->stream)) {
  7705. $this->stream = null;
  7706. throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$errorMessage, $this->url));
  7707. }
  7708. }
  7709. fwrite($this->stream, (string) $record['formatted']);
  7710. }
  7711. }
  7712. }
  7713. namespace Monolog\Handler
  7714. {
  7715. use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
  7716. use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
  7717. use Monolog\Logger;
  7718. class FingersCrossedHandler extends AbstractHandler
  7719. {
  7720. protected $handler;
  7721. protected $activationStrategy;
  7722. protected $buffering = true;
  7723. protected $bufferSize;
  7724. protected $buffer = array();
  7725. protected $stopBuffering;
  7726. public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true)
  7727. {
  7728. if (null === $activationStrategy) {
  7729. $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
  7730. }
  7731. if (!$activationStrategy instanceof ActivationStrategyInterface) {
  7732. $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy);
  7733. }
  7734. $this->handler = $handler;
  7735. $this->activationStrategy = $activationStrategy;
  7736. $this->bufferSize = $bufferSize;
  7737. $this->bubble = $bubble;
  7738. $this->stopBuffering = $stopBuffering;
  7739. }
  7740. public function isHandling(array $record)
  7741. {
  7742. return true;
  7743. }
  7744. public function handle(array $record)
  7745. {
  7746. if ($this->buffering) {
  7747. $this->buffer[] = $record;
  7748. if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) {
  7749. array_shift($this->buffer);
  7750. }
  7751. if ($this->activationStrategy->isHandlerActivated($record)) {
  7752. if ($this->stopBuffering) {
  7753. $this->buffering = false;
  7754. }
  7755. if (!$this->handler instanceof HandlerInterface) {
  7756. $this->handler = call_user_func($this->handler, $record, $this);
  7757. }
  7758. if (!$this->handler instanceof HandlerInterface) {
  7759. throw new \RuntimeException("The factory callback should return a HandlerInterface");
  7760. }
  7761. $this->handler->handleBatch($this->buffer);
  7762. $this->buffer = array();
  7763. }
  7764. } else {
  7765. $this->handler->handle($record);
  7766. }
  7767. return false === $this->bubble;
  7768. }
  7769. public function reset()
  7770. {
  7771. $this->buffering = true;
  7772. }
  7773. }
  7774. }
  7775. namespace Monolog\Handler
  7776. {
  7777. use Monolog\Logger;
  7778. class TestHandler extends AbstractProcessingHandler
  7779. {
  7780. protected $records = array();
  7781. protected $recordsByLevel = array();
  7782. public function getRecords()
  7783. {
  7784. return $this->records;
  7785. }
  7786. public function hasEmergency($record)
  7787. {
  7788. return $this->hasRecord($record, Logger::EMERGENCY);
  7789. }
  7790. public function hasAlert($record)
  7791. {
  7792. return $this->hasRecord($record, Logger::ALERT);
  7793. }
  7794. public function hasCritical($record)
  7795. {
  7796. return $this->hasRecord($record, Logger::CRITICAL);
  7797. }
  7798. public function hasError($record)
  7799. {
  7800. return $this->hasRecord($record, Logger::ERROR);
  7801. }
  7802. public function hasWarning($record)
  7803. {
  7804. return $this->hasRecord($record, Logger::WARNING);
  7805. }
  7806. public function hasNotice($record)
  7807. {
  7808. return $this->hasRecord($record, Logger::NOTICE);
  7809. }
  7810. public function hasInfo($record)
  7811. {
  7812. return $this->hasRecord($record, Logger::INFO);
  7813. }
  7814. public function hasDebug($record)
  7815. {
  7816. return $this->hasRecord($record, Logger::DEBUG);
  7817. }
  7818. public function hasEmergencyRecords()
  7819. {
  7820. return isset($this->recordsByLevel[Logger::EMERGENCY]);
  7821. }
  7822. public function hasAlertRecords()
  7823. {
  7824. return isset($this->recordsByLevel[Logger::ALERT]);
  7825. }
  7826. public function hasCriticalRecords()
  7827. {
  7828. return isset($this->recordsByLevel[Logger::CRITICAL]);
  7829. }
  7830. public function hasErrorRecords()
  7831. {
  7832. return isset($this->recordsByLevel[Logger::ERROR]);
  7833. }
  7834. public function hasWarningRecords()
  7835. {
  7836. return isset($this->recordsByLevel[Logger::WARNING]);
  7837. }
  7838. public function hasNoticeRecords()
  7839. {
  7840. return isset($this->recordsByLevel[Logger::NOTICE]);
  7841. }
  7842. public function hasInfoRecords()
  7843. {
  7844. return isset($this->recordsByLevel[Logger::INFO]);
  7845. }
  7846. public function hasDebugRecords()
  7847. {
  7848. return isset($this->recordsByLevel[Logger::DEBUG]);
  7849. }
  7850. protected function hasRecord($record, $level)
  7851. {
  7852. if (!isset($this->recordsByLevel[$level])) {
  7853. return false;
  7854. }
  7855. if (is_array($record)) {
  7856. $record = $record['message'];
  7857. }
  7858. foreach ($this->recordsByLevel[$level] as $rec) {
  7859. if ($rec['message'] === $record) {
  7860. return true;
  7861. }
  7862. }
  7863. return false;
  7864. }
  7865. protected function write(array $record)
  7866. {
  7867. $this->recordsByLevel[$record['level']][] = $record;
  7868. $this->records[] = $record;
  7869. }
  7870. }
  7871. }
  7872. namespace Monolog
  7873. {
  7874. use Monolog\Handler\HandlerInterface;
  7875. use Monolog\Handler\StreamHandler;
  7876. class Logger
  7877. {
  7878. const DEBUG = 100;
  7879. const INFO = 200;
  7880. const NOTICE = 250;
  7881. const WARNING = 300;
  7882. const ERROR = 400;
  7883. const CRITICAL = 500;
  7884. const ALERT = 550;
  7885. const EMERGENCY = 600;
  7886. protected static $levels = array(
  7887. 100 => 'DEBUG',
  7888. 200 => 'INFO',
  7889. 250 => 'NOTICE',
  7890. 300 => 'WARNING',
  7891. 400 => 'ERROR',
  7892. 500 => 'CRITICAL',
  7893. 550 => 'ALERT',
  7894. 600 => 'EMERGENCY',
  7895. );
  7896. protected static $timezone;
  7897. protected $name;
  7898. protected $handlers = array();
  7899. protected $processors = array();
  7900. public function __construct($name)
  7901. {
  7902. $this->name = $name;
  7903. if (!self::$timezone) {
  7904. self::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
  7905. }
  7906. }
  7907. public function getName()
  7908. {
  7909. return $this->name;
  7910. }
  7911. public function pushHandler(HandlerInterface $handler)
  7912. {
  7913. array_unshift($this->handlers, $handler);
  7914. }
  7915. public function popHandler()
  7916. {
  7917. if (!$this->handlers) {
  7918. throw new \LogicException('You tried to pop from an empty handler stack.');
  7919. }
  7920. return array_shift($this->handlers);
  7921. }
  7922. public function pushProcessor($callback)
  7923. {
  7924. if (!is_callable($callback)) {
  7925. throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
  7926. }
  7927. array_unshift($this->processors, $callback);
  7928. }
  7929. public function popProcessor()
  7930. {
  7931. if (!$this->processors) {
  7932. throw new \LogicException('You tried to pop from an empty processor stack.');
  7933. }
  7934. return array_shift($this->processors);
  7935. }
  7936. public function addRecord($level, $message, array $context = array())
  7937. {
  7938. if (!$this->handlers) {
  7939. $this->pushHandler(new StreamHandler('php://stderr', self::DEBUG));
  7940. }
  7941. $record = array(
  7942. 'message' => (string) $message,
  7943. 'context' => $context,
  7944. 'level' => $level,
  7945. 'level_name' => self::getLevelName($level),
  7946. 'channel' => $this->name,
  7947. 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)))->setTimeZone(self::$timezone),
  7948. 'extra' => array(),
  7949. );
  7950. $handlerKey = null;
  7951. foreach ($this->handlers as $key => $handler) {
  7952. if ($handler->isHandling($record)) {
  7953. $handlerKey = $key;
  7954. break;
  7955. }
  7956. }
  7957. if (null === $handlerKey) {
  7958. return false;
  7959. }
  7960. foreach ($this->processors as $processor) {
  7961. $record = call_user_func($processor, $record);
  7962. }
  7963. while (isset($this->handlers[$handlerKey]) &&
  7964. false === $this->handlers[$handlerKey]->handle($record)) {
  7965. $handlerKey++;
  7966. }
  7967. return true;
  7968. }
  7969. public function addDebug($message, array $context = array())
  7970. {
  7971. return $this->addRecord(self::DEBUG, $message, $context);
  7972. }
  7973. public function addInfo($message, array $context = array())
  7974. {
  7975. return $this->addRecord(self::INFO, $message, $context);
  7976. }
  7977. public function addNotice($message, array $context = array())
  7978. {
  7979. return $this->addRecord(self::NOTICE, $message, $context);
  7980. }
  7981. public function addWarning($message, array $context = array())
  7982. {
  7983. return $this->addRecord(self::WARNING, $message, $context);
  7984. }
  7985. public function addError($message, array $context = array())
  7986. {
  7987. return $this->addRecord(self::ERROR, $message, $context);
  7988. }
  7989. public function addCritical($message, array $context = array())
  7990. {
  7991. return $this->addRecord(self::CRITICAL, $message, $context);
  7992. }
  7993. public function addAlert($message, array $context = array())
  7994. {
  7995. return $this->addRecord(self::ALERT, $message, $context);
  7996. }
  7997. public function addEmergency($message, array $context = array())
  7998. {
  7999. return $this->addRecord(self::EMERGENCY, $message, $context);
  8000. }
  8001. public static function getLevelName($level)
  8002. {
  8003. return self::$levels[$level];
  8004. }
  8005. public function isHandling($level)
  8006. {
  8007. $record = array(
  8008. 'message' => '',
  8009. 'context' => array(),
  8010. 'level' => $level,
  8011. 'level_name' => self::getLevelName($level),
  8012. 'channel' => $this->name,
  8013. 'datetime' => new \DateTime(),
  8014. 'extra' => array(),
  8015. );
  8016. foreach ($this->handlers as $key => $handler) {
  8017. if ($handler->isHandling($record)) {
  8018. return true;
  8019. }
  8020. }
  8021. return false;
  8022. }
  8023. public function debug($message, array $context = array())
  8024. {
  8025. return $this->addRecord(self::DEBUG, $message, $context);
  8026. }
  8027. public function info($message, array $context = array())
  8028. {
  8029. return $this->addRecord(self::INFO, $message, $context);
  8030. }
  8031. public function notice($message, array $context = array())
  8032. {
  8033. return $this->addRecord(self::NOTICE, $message, $context);
  8034. }
  8035. public function warn($message, array $context = array())
  8036. {
  8037. return $this->addRecord(self::WARNING, $message, $context);
  8038. }
  8039. public function err($message, array $context = array())
  8040. {
  8041. return $this->addRecord(self::ERROR, $message, $context);
  8042. }
  8043. public function crit($message, array $context = array())
  8044. {
  8045. return $this->addRecord(self::CRITICAL, $message, $context);
  8046. }
  8047. public function alert($message, array $context = array())
  8048. {
  8049. return $this->addRecord(self::ALERT, $message, $context);
  8050. }
  8051. public function emerg($message, array $context = array())
  8052. {
  8053. return $this->addRecord(self::EMERGENCY, $message, $context);
  8054. }
  8055. }
  8056. }
  8057. namespace Symfony\Component\HttpKernel\Log
  8058. {
  8059. interface LoggerInterface
  8060. {
  8061. public function emerg($message, array $context = array());
  8062. public function alert($message, array $context = array());
  8063. public function crit($message, array $context = array());
  8064. public function err($message, array $context = array());
  8065. public function warn($message, array $context = array());
  8066. public function notice($message, array $context = array());
  8067. public function info($message, array $context = array());
  8068. public function debug($message, array $context = array());
  8069. }
  8070. }
  8071. namespace Symfony\Component\HttpKernel\Log
  8072. {
  8073. interface DebugLoggerInterface
  8074. {
  8075. public function getLogs();
  8076. public function countErrors();
  8077. }
  8078. }
  8079. namespace Symfony\Bridge\Monolog
  8080. {
  8081. use Monolog\Logger as BaseLogger;
  8082. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  8083. use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
  8084. class Logger extends BaseLogger implements LoggerInterface, DebugLoggerInterface
  8085. {
  8086. public function getLogs()
  8087. {
  8088. if ($logger = $this->getDebugLogger()) {
  8089. return $logger->getLogs();
  8090. }
  8091. }
  8092. public function countErrors()
  8093. {
  8094. if ($logger = $this->getDebugLogger()) {
  8095. return $logger->countErrors();
  8096. }
  8097. }
  8098. private function getDebugLogger()
  8099. {
  8100. foreach ($this->handlers as $handler) {
  8101. if ($handler instanceof DebugLoggerInterface) {
  8102. return $handler;
  8103. }
  8104. }
  8105. }
  8106. }
  8107. }
  8108. namespace Symfony\Bridge\Monolog\Handler
  8109. {
  8110. use Monolog\Logger;
  8111. use Monolog\Handler\TestHandler;
  8112. use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
  8113. class DebugHandler extends TestHandler implements DebugLoggerInterface
  8114. {
  8115. public function getLogs()
  8116. {
  8117. $records = array();
  8118. foreach ($this->records as $record) {
  8119. $records[] = array(
  8120. 'timestamp' => $record['datetime']->getTimestamp(),
  8121. 'message' => $record['message'],
  8122. 'priority' => $record['level'],
  8123. 'priorityName' => $record['level_name'],
  8124. 'context' => $record['context'],
  8125. );
  8126. }
  8127. return $records;
  8128. }
  8129. public function countErrors()
  8130. {
  8131. $cnt = 0;
  8132. $levels = array(Logger::ERROR, Logger::CRITICAL, Logger::ALERT);
  8133. if (defined('Monolog\Logger::EMERGENCY')) {
  8134. $levels[] = Logger::EMERGENCY;
  8135. }
  8136. foreach ($levels as $level) {
  8137. if (isset($this->recordsByLevel[$level])) {
  8138. $cnt += count($this->recordsByLevel[$level]);
  8139. }
  8140. }
  8141. return $cnt;
  8142. }
  8143. }
  8144. }
  8145. namespace JMS\DiExtraBundle\HttpKernel
  8146. {
  8147. use Metadata\ClassHierarchyMetadata;
  8148. use JMS\DiExtraBundle\Metadata\ClassMetadata;
  8149. use CG\Core\DefaultNamingStrategy;
  8150. use CG\Proxy\Enhancer;
  8151. use JMS\AopBundle\DependencyInjection\Compiler\PointcutMatchingPass;
  8152. use JMS\DiExtraBundle\Generator\DefinitionInjectorGenerator;
  8153. use JMS\DiExtraBundle\Generator\LookupMethodClassGenerator;
  8154. use JMS\DiExtraBundle\DependencyInjection\Dumper\PhpDumper;
  8155. use Metadata\MetadataFactory;
  8156. use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
  8157. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  8158. use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
  8159. use Symfony\Component\DependencyInjection\Parameter;
  8160. use Symfony\Component\DependencyInjection\Reference;
  8161. use Symfony\Component\DependencyInjection\Definition;
  8162. use Symfony\Component\Config\Resource\FileResource;
  8163. use Symfony\Component\DependencyInjection\ContainerBuilder;
  8164. use Symfony\Component\Config\ConfigCache;
  8165. use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver as BaseControllerResolver;
  8166. class ControllerResolver extends BaseControllerResolver
  8167. {
  8168. protected function createController($controller)
  8169. {
  8170. if (false === $pos = strpos($controller, '::')) {
  8171. $count = substr_count($controller, ':');
  8172. if (2 == $count) {
  8173. $controller = $this->parser->parse($controller);
  8174. $pos = strpos($controller, '::');
  8175. } elseif (1 == $count) {
  8176. list($service, $method) = explode(':', $controller);
  8177. return array($this->container->get($service), $method);
  8178. } else {
  8179. throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
  8180. }
  8181. }
  8182. $class = substr($controller, 0, $pos);
  8183. $method = substr($controller, $pos+2);
  8184. if (!class_exists($class)) {
  8185. throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
  8186. }
  8187. $injector = $this->createInjector($class);
  8188. $controller = call_user_func($injector, $this->container);
  8189. if ($controller instanceof ContainerAwareInterface) {
  8190. $controller->setContainer($this->container);
  8191. }
  8192. return array($controller, $method);
  8193. }
  8194. public function createInjector($class)
  8195. {
  8196. $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/controller_injectors/'.str_replace('\\', '', $class).'.php';
  8197. $cache = new ConfigCache($filename, $this->container->getParameter('kernel.debug'));
  8198. if (!$cache->isFresh()) {
  8199. $metadata = $this->container->get('jms_di_extra.metadata.metadata_factory')->getMetadataForClass($class);
  8200. if (null === $metadata) {
  8201. $metadata = new ClassHierarchyMetadata();
  8202. $metadata->addClassMetadata(new ClassMetadata($class));
  8203. }
  8204. if (null !== $metadata->getOutsideClassMetadata()->id
  8205. && 0 !== strpos($metadata->getOutsideClassMetadata()->id, '_jms_di_extra.unnamed.service')) {
  8206. return;
  8207. }
  8208. $this->prepareContainer($cache, $filename, $metadata, $class);
  8209. }
  8210. if ( ! class_exists($class.'__JMSInjector', false)) {
  8211. require $filename;
  8212. }
  8213. return array($class.'__JMSInjector', 'inject');
  8214. }
  8215. private function prepareContainer($cache, $containerFilename, $metadata, $className)
  8216. {
  8217. $container = new ContainerBuilder();
  8218. $container->setParameter('jms_aop.cache_dir', $this->container->getParameter('jms_di_extra.cache_dir'));
  8219. $def = $container
  8220. ->register('jms_aop.interceptor_loader', 'JMS\AopBundle\Aop\InterceptorLoader')
  8221. ->addArgument(new Reference('service_container'))
  8222. ->setPublic(false)
  8223. ;
  8224. $ref = $metadata->getOutsideClassMetadata()->reflection;
  8225. while ($ref && false !== $filename = $ref->getFilename()) {
  8226. $container->addResource(new FileResource($filename));
  8227. $ref = $ref->getParentClass();
  8228. }
  8229. $definitions = $this->container->get('jms_di_extra.metadata.converter')->convert($metadata);
  8230. $serviceIds = $parameters = array();
  8231. $controllerDef = array_pop($definitions);
  8232. $container->setDefinition('controller', $controllerDef);
  8233. foreach ($definitions as $id => $def) {
  8234. $container->setDefinition($id, $def);
  8235. }
  8236. $this->generateLookupMethods($controllerDef, $metadata);
  8237. $config = $container->getCompilerPassConfig();
  8238. $config->setOptimizationPasses(array());
  8239. $config->setRemovingPasses(array());
  8240. $config->addPass(new ResolveDefinitionTemplatesPass());
  8241. $config->addPass(new PointcutMatchingPass($this->container->get('jms_aop.pointcut_container')->getPointcuts()));
  8242. $config->addPass(new InlineServiceDefinitionsPass());
  8243. $container->compile();
  8244. if (!file_exists($dir = dirname($containerFilename))) {
  8245. if (false === @mkdir($dir, 0777, true)) {
  8246. throw new \RuntimeException(sprintf('Could not create directory "%s".', $dir));
  8247. }
  8248. }
  8249. static $generator;
  8250. if (null === $generator) {
  8251. $generator = new DefinitionInjectorGenerator();
  8252. }
  8253. $cache->write($generator->generate($container->getDefinition('controller'), $className), $container->getResources());
  8254. }
  8255. private function generateLookupMethods($def, $metadata)
  8256. {
  8257. $found = false;
  8258. foreach ($metadata->classMetadata as $cMetadata) {
  8259. if (!empty($cMetadata->lookupMethods)) {
  8260. $found = true;
  8261. break;
  8262. }
  8263. }
  8264. if (!$found) {
  8265. return;
  8266. }
  8267. $generator = new LookupMethodClassGenerator($metadata);
  8268. $outerClass = $metadata->getOutsideClassMetadata()->reflection;
  8269. if ($file = $def->getFile()) {
  8270. $generator->setRequiredFile($file);
  8271. }
  8272. $enhancer = new Enhancer(
  8273. $outerClass,
  8274. array(),
  8275. array(
  8276. $generator,
  8277. )
  8278. );
  8279. $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/lookup_method_classes/'.str_replace('\\', '-', $outerClass->name).'.php';
  8280. $enhancer->writeClass($filename);
  8281. $def->setFile($filename);
  8282. $def->setClass($enhancer->getClassName($outerClass));
  8283. $def->addMethodCall('__jmsDiExtra_setContainer', array(new Reference('service_container')));
  8284. }
  8285. }
  8286. }