gl-website-deployer/vendor/doctrine/rst-parser/lib/FileIncluder.php
2024-11-19 08:02:04 +01:00

95 lines
2.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Doctrine\RST;
use RuntimeException;
use function explode;
use function file_exists;
use function file_get_contents;
use function is_readable;
use function preg_replace_callback;
use function realpath;
use function sprintf;
use function strpos;
final class FileIncluder
{
/** @var Environment */
private $environment;
/** @var bool */
private $includeAllowed = true;
/** @var string */
private $includeRoot;
public function __construct(
Environment $environment,
bool $includeAllowed,
string $includeRoot
) {
$this->environment = $environment;
$this->includeAllowed = $includeAllowed;
$this->includeRoot = $includeRoot;
}
public function includeFiles(string $document): string
{
return (string) preg_replace_callback(
'/^\.\. include:: (.+)$/m',
function ($match): string {
$path = $this->environment->absoluteRelativePath($match[1]);
if (! file_exists($path)) {
throw new RuntimeException(sprintf('Include "%s" does not exist or is not readable.', $match[0]));
}
if ($this->isFileIncludeAllowed($path)) {
$contents = file_get_contents($path);
if ($contents === false) {
throw new RuntimeException(sprintf('Could not load file from path %s', $path));
}
return $this->includeFiles($contents);
}
return '';
},
$document
);
}
private function isFileIncludeAllowed(string $path): bool
{
if (! $this->includeAllowed) {
return false;
}
if (! @is_readable($path)) {
return false;
}
if ($this->includeRoot === '') {
return true;
}
$real = realpath($path);
if ($real === false) {
return false;
}
foreach (explode(':', $this->includeRoot) as $root) {
if (strpos($real, $root) === 0) {
return true;
}
}
return false;
}
}