Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\HttpKernel\Fragment; Chris@0: Chris@0: use Symfony\Component\HttpFoundation\Request; Chris@0: use Symfony\Component\HttpFoundation\Response; Chris@0: use Symfony\Component\HttpKernel\Controller\ControllerReference; Chris@0: use Symfony\Component\HttpKernel\UriSigner; Chris@17: use Symfony\Component\Templating\EngineInterface; Chris@12: use Twig\Environment; Chris@12: use Twig\Error\LoaderError; Chris@12: use Twig\Loader\ExistsLoaderInterface; Chris@0: Chris@0: /** Chris@0: * Implements the Hinclude rendering strategy. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: */ Chris@0: class HIncludeFragmentRenderer extends RoutableFragmentRenderer Chris@0: { Chris@0: private $globalDefaultTemplate; Chris@0: private $signer; Chris@0: private $templating; Chris@0: private $charset; Chris@0: Chris@0: /** Chris@12: * @param EngineInterface|Environment $templating An EngineInterface or a Twig instance Chris@12: * @param UriSigner $signer A UriSigner instance Chris@12: * @param string $globalDefaultTemplate The global default content (it can be a template name or the content) Chris@12: * @param string $charset Chris@0: */ Chris@0: public function __construct($templating = null, UriSigner $signer = null, $globalDefaultTemplate = null, $charset = 'utf-8') Chris@0: { Chris@0: $this->setTemplating($templating); Chris@0: $this->globalDefaultTemplate = $globalDefaultTemplate; Chris@0: $this->signer = $signer; Chris@0: $this->charset = $charset; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the templating engine to use to render the default content. Chris@0: * Chris@12: * @param EngineInterface|Environment|null $templating An EngineInterface or an Environment instance Chris@0: * Chris@0: * @throws \InvalidArgumentException Chris@0: */ Chris@0: public function setTemplating($templating) Chris@0: { Chris@12: if (null !== $templating && !$templating instanceof EngineInterface && !$templating instanceof Environment) { Chris@12: throw new \InvalidArgumentException('The hinclude rendering strategy needs an instance of Twig\Environment or Symfony\Component\Templating\EngineInterface'); Chris@0: } Chris@0: Chris@0: $this->templating = $templating; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if a templating engine has been set. Chris@0: * Chris@0: * @return bool true if the templating engine has been set, false otherwise Chris@0: */ Chris@0: public function hasTemplating() Chris@0: { Chris@0: return null !== $this->templating; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * Additional available options: Chris@0: * Chris@0: * * default: The default content (it can be a template name or the content) Chris@0: * * id: An optional hx:include tag id attribute Chris@0: * * attributes: An optional array of hx:include tag attributes Chris@0: */ Chris@17: public function render($uri, Request $request, array $options = []) Chris@0: { Chris@0: if ($uri instanceof ControllerReference) { Chris@0: if (null === $this->signer) { Chris@0: throw new \LogicException('You must use a proper URI when using the Hinclude rendering strategy or set a URL signer.'); Chris@0: } Chris@0: Chris@0: // we need to sign the absolute URI, but want to return the path only. Chris@17: $uri = substr($this->signer->sign($this->generateFragmentUri($uri, $request, true)), \strlen($request->getSchemeAndHttpHost())); Chris@0: } Chris@0: Chris@0: // We need to replace ampersands in the URI with the encoded form in order to return valid html/xml content. Chris@0: $uri = str_replace('&', '&', $uri); Chris@0: Chris@0: $template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate; Chris@0: if (null !== $this->templating && $template && $this->templateExists($template)) { Chris@0: $content = $this->templating->render($template); Chris@0: } else { Chris@0: $content = $template; Chris@0: } Chris@0: Chris@17: $attributes = isset($options['attributes']) && \is_array($options['attributes']) ? $options['attributes'] : []; Chris@0: if (isset($options['id']) && $options['id']) { Chris@0: $attributes['id'] = $options['id']; Chris@0: } Chris@0: $renderedAttributes = ''; Chris@17: if (\count($attributes) > 0) { Chris@0: $flags = ENT_QUOTES | ENT_SUBSTITUTE; Chris@0: foreach ($attributes as $attribute => $value) { Chris@0: $renderedAttributes .= sprintf( Chris@0: ' %s="%s"', Chris@0: htmlspecialchars($attribute, $flags, $this->charset, false), Chris@0: htmlspecialchars($value, $flags, $this->charset, false) Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: return new Response(sprintf('%s', $uri, $renderedAttributes, $content)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param string $template Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: private function templateExists($template) Chris@0: { Chris@0: if ($this->templating instanceof EngineInterface) { Chris@0: try { Chris@0: return $this->templating->exists($template); Chris@17: } catch (\Exception $e) { Chris@0: return false; Chris@0: } Chris@0: } Chris@0: Chris@0: $loader = $this->templating->getLoader(); Chris@12: if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) { Chris@0: return $loader->exists($template); Chris@0: } Chris@0: Chris@0: try { Chris@0: if (method_exists($loader, 'getSourceContext')) { Chris@0: $loader->getSourceContext($template); Chris@0: } else { Chris@0: $loader->getSource($template); Chris@0: } Chris@0: Chris@0: return true; Chris@12: } catch (LoaderError $e) { Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getName() Chris@0: { Chris@0: return 'hinclude'; Chris@0: } Chris@0: }