Show the source code of any file in a Symfony project

Published on 2019-02-08 • Modified on 2019-02-10

Well, it's in fact the Twig helper that I am using on this website to show all the snippets. You already have a native source Twig helper but it can only be used to show source of templates. This one is more generic and will allow you to show the source of any file of your project. (therefore, be sure that the parameters you pass to it are secured). At top you see the content of the _15.html.twig template dumped with the native one and below the source of the new one which is therefore used to display it's own content.
PS: You can remove the str_replace() part as it is specific to this project. Check out the code of a Symfony runnable snippet to understand why. 😉


{{ source('snippet/code/_15.html.twig')|escape }}
{{ source_php('src/Twig/Extension/SourceExtension.php', false)|escape }}
{# End of _15.html.twig Twig template #}

<?php declare(strict_types=1);

namespace App\Twig\Extension;

use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
 * Source helpers for snippets.
 */
final class SourceExtension extends AbstractExtension
{
    private $kernel;
    private $translator;

    public function __construct(KernelInterface $kernel, TranslatorInterface $translator)
    {
        $this->kernel = $kernel;
        $this->translator = $translator;
    }

    public function getFunctions(): array
    {
        return [
            new TwigFunction('source_php', [$this, 'getSource']),
            new TwigFunction('source_part', [$this, 'getSourcePart']),
        ];
    }

    /**
     * @deprecated Use source_part
     */
    public function getSource(string $file, bool $replaceDocBlock = true, int $linesLimit = null): string
    {
        $filename = $this->kernel->getProjectDir().'/'.$file;
        if (!file_exists($filename)) {
            throw new \InvalidArgumentException(sprintf('File "%s" not found.', $file));
        }

        // Only get x first lines only
        if (\is_int($linesLimit)) {
            $lines = file($filename);
            $source = implode('', \array_slice(\is_array($lines) ? $lines : [], 0, $linesLimit));
        } else {
            $source = file_get_contents($filename);
        }

        if ($source && $replaceDocBlock) {
            $this->replaceDocBlocks($source);
        }

        return (string) $source;
    }

    /**
     * Can show a exact part of a file.
     */
    public function getSourcePart(string $file, bool $replaceDocBlock = true, int $start = 1, int $end = null): string
    {
        $filename = $this->kernel->getProjectDir().'/'.$file;
        if (!file_exists($filename)) {
            throw new \InvalidArgumentException(sprintf('File "%s" not found.', $file));
        }
        $sourceArr = file($filename);

        // Take the last line
        if ($end === null) {
            $end = \count($sourceArr ?: []);
        }

        $source = implode('', \array_slice(\is_array($sourceArr) ? $sourceArr : [], $start-1, $end-$start+1));
        if ($source && $replaceDocBlock) {
            $source = $this->replaceDocBlocks($source);
        }

        return (string) $source;
    }

    private function replaceDocBlocks(string $source)
    {
        $search = [
            '%docblock1%' => $this->translator->trans('docblock1', [], 'snippet'),
            '%docblock2%' => $this->translator->trans('docblock2', [], 'snippet'),
            '%docblock3%' => $this->translator->trans('docblock3', [], 'snippet'),
            '%docblock4%' => $this->translator->trans('docblock4', [], 'snippet'),
        ];

        return str_replace(array_keys($search), $search, $source);
    }
}

 More on Stackoverflow