156 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php namespace MapGuesser\View;
 | 
						|
 | 
						|
class Linker
 | 
						|
{
 | 
						|
    const INLINE_ASSET_LIMIT = 2000;
 | 
						|
 | 
						|
    private string $view;
 | 
						|
 | 
						|
    public function __construct(string $view)
 | 
						|
    {
 | 
						|
        $this->view = $view;
 | 
						|
    }
 | 
						|
 | 
						|
    public function generate(): void
 | 
						|
    {
 | 
						|
        $input = ROOT . '/views/' . $this->view . '.php';
 | 
						|
 | 
						|
        $temporaryFiles = [];
 | 
						|
        $sections = ['externalCss' => '', 'inlineCss' => '', 'externalJs' => '', 'inlineJs' => ''];
 | 
						|
        $extra = ['', ''];
 | 
						|
 | 
						|
        do {
 | 
						|
            $parser = new Parser($input);
 | 
						|
            $fragment = $parser->parse();
 | 
						|
 | 
						|
            $extends = $fragment->getExtends();
 | 
						|
 | 
						|
            $this->generateAssets($fragment, $sections);
 | 
						|
 | 
						|
            $sections = array_merge($sections, $fragment->getSections()); //TODO: detect if section defined multiple times
 | 
						|
            $extra[0] = $fragment->getExtra()[0] . $extra[0];
 | 
						|
            $extra[1] = $extra[1] . $fragment->getExtra()[1];
 | 
						|
 | 
						|
            if ($extends === null) {
 | 
						|
                $this->writeFinal($extra, $input, ROOT . '/cache/views/' . $this->view . '.php');
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            $tmpFile = tempnam(sys_get_temp_dir(), 'mapg-view-');
 | 
						|
            $temporaryFiles[] = $tmpFile;
 | 
						|
 | 
						|
            $this->extendTemplate($sections, ROOT . '/views/' . $extends . '.php', $tmpFile);
 | 
						|
 | 
						|
            $input = $tmpFile;
 | 
						|
        } while (true);
 | 
						|
 | 
						|
        foreach ($temporaryFiles as $tmpFile) {
 | 
						|
            unlink($tmpFile);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private function extendTemplate(array $sections, string $file, string $output): void
 | 
						|
    {
 | 
						|
        $inputFileHandle = fopen($file, 'r');
 | 
						|
        if (!$inputFileHandle) {
 | 
						|
            throw new \Exception('Cannot open file ' . $file);
 | 
						|
        }
 | 
						|
 | 
						|
        $outputFileHandle = fopen($output, 'w');
 | 
						|
        if (!$outputFileHandle) {
 | 
						|
            throw new \Exception('Cannot open file ' . $output . 'for writing.');
 | 
						|
        }
 | 
						|
 | 
						|
        $lineNumber = 0;
 | 
						|
        while (($line = fgets($inputFileHandle)) !== false) {
 | 
						|
            ++$lineNumber;
 | 
						|
 | 
						|
            if (preg_match('/^\s*@yields\(\'([\w\/]+)\'\)\s*$/', $line, $matches)) {
 | 
						|
                if (isset($sections[$matches[1]])) {
 | 
						|
                    fwrite($outputFileHandle, $sections[$matches[1]]);
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                fwrite($outputFileHandle, $line);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        fclose($inputFileHandle);
 | 
						|
        fclose($outputFileHandle);
 | 
						|
    }
 | 
						|
 | 
						|
    private function writeFinal(array $extra, string $file, string $output): void
 | 
						|
    {
 | 
						|
        $dirname = pathinfo($output, PATHINFO_DIRNAME);
 | 
						|
        if (!is_dir($dirname)) {
 | 
						|
            mkdir($dirname, 0755, true);
 | 
						|
        }
 | 
						|
 | 
						|
        $inputFileHandle = fopen($file, 'r');
 | 
						|
        if (!$inputFileHandle) {
 | 
						|
            throw new \Exception('Cannot open file ' . $file);
 | 
						|
        }
 | 
						|
 | 
						|
        $outputFileHandle = fopen($output, 'w');
 | 
						|
        if (!$outputFileHandle) {
 | 
						|
            throw new \Exception('Cannot open file ' . $output . 'for writing.');
 | 
						|
        }
 | 
						|
 | 
						|
        fwrite($outputFileHandle, $extra[0]);
 | 
						|
        while (($line = fgets($inputFileHandle)) !== false) {
 | 
						|
            fwrite($outputFileHandle, $line);
 | 
						|
        }
 | 
						|
        fwrite($outputFileHandle, $extra[1]);
 | 
						|
 | 
						|
        fclose($inputFileHandle);
 | 
						|
        fclose($outputFileHandle);
 | 
						|
    }
 | 
						|
 | 
						|
    private function generateAssets(ParsedFragment $fragment, array &$sections)
 | 
						|
    {
 | 
						|
        foreach ($fragment->getCss() as $cssFile) {
 | 
						|
            $asset = $this->parseAsset($cssFile, 'css');
 | 
						|
            if (isset($asset['code'])) {
 | 
						|
                $sections['inlineCss'] .= '<style>' . PHP_EOL;
 | 
						|
                $sections['inlineCss'] .= $asset['code'];
 | 
						|
                $sections['inlineCss'] .= '</style>' . PHP_EOL;
 | 
						|
            } elseif (isset($asset['file'])) {
 | 
						|
                $sections['externalCss'] .= '<link href="' . $asset['file'] . '" rel="stylesheet">' . PHP_EOL;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        foreach ($fragment->getJs() as $jsFile) {
 | 
						|
            $asset = $this->parseAsset($jsFile, 'js');
 | 
						|
            if (isset($asset['code'])) {
 | 
						|
                $sections['inlineJs'] .= '<script>' . PHP_EOL;
 | 
						|
                $sections['inlineJs'] .= $asset['code'];
 | 
						|
                $sections['inlineJs'] .= '</script>' . PHP_EOL;
 | 
						|
            } elseif (isset($asset['file'])) {
 | 
						|
                $sections['externalJs'] .= '<script src="' . $asset['file'] . '"></script>' . PHP_EOL;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private function parseAsset(string $asset, string $type): array
 | 
						|
    {
 | 
						|
        $output = [];
 | 
						|
 | 
						|
        eval('$asset = ' . $asset . ';');
 | 
						|
 | 
						|
        if (
 | 
						|
            empty($_ENV['DEV']) &&
 | 
						|
            preg_match('/^' . $type . '\/.*/', $asset) &&
 | 
						|
            filesize(ROOT . '/public/static/' . $asset) < self::INLINE_ASSET_LIMIT
 | 
						|
        ) {
 | 
						|
            $output['code'] = file_get_contents(ROOT . '/public/static/' . $asset);
 | 
						|
        } else {
 | 
						|
            if (!preg_match('/^http(s)?/', $asset)) {
 | 
						|
                $output['file'] = $_ENV['STATIC_ROOT'] .  '/' . $asset . '?rev=<?= REVISION ?>';
 | 
						|
            } else {
 | 
						|
                $output['file'] = $asset;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $output;
 | 
						|
    }
 | 
						|
}
 |