PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 2ms

/app/cache/prod/classes.php

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