1
0
mirror of https://github.com/flarum/core.git synced 2025-08-06 16:36:47 +02:00

Harden formatter

- prevents __PHP_Incomplete_Class
- stores a revision
This commit is contained in:
Daniel Klabbers
2021-04-22 14:05:37 +02:00
parent 43d6b3104d
commit cd0a31a6e7
2 changed files with 91 additions and 10 deletions

View File

@@ -9,12 +9,17 @@
namespace Flarum\Formatter; namespace Flarum\Formatter;
use __PHP_Incomplete_Class;
use ArrayObject;
use Flarum\Frontend\Compiler\RevisionCompiler;
use Illuminate\Contracts\Cache\Repository; use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use s9e\TextFormatter\Configurator; use s9e\TextFormatter\Configurator;
use s9e\TextFormatter\Unparser; use s9e\TextFormatter\Unparser;
class Formatter class Formatter extends RevisionCompiler
{ {
protected $configurationCallbacks = []; protected $configurationCallbacks = [];
@@ -35,13 +40,17 @@ class Formatter
protected $cacheDir; protected $cacheDir;
/** /**
* @param Repository $cache * @var array|null
* @param string $cacheDir
*/ */
public function __construct(Repository $cache, $cacheDir) protected static $formatter;
public function __construct(Repository $cache, string $cacheDir, Filesystem $assetsDir)
{ {
$this->cache = $cache; $this->cache = $cache;
$this->cacheDir = $cacheDir; $this->cacheDir = $cacheDir;
$this->assetsDir = $assetsDir;
$this->filename = 'formatter';
} }
public function addConfigurationCallback($callback) public function addConfigurationCallback($callback)
@@ -178,13 +187,27 @@ class Formatter
* @param string $name "renderer" or "parser" or "js" * @param string $name "renderer" or "parser" or "js"
* @return mixed * @return mixed
*/ */
protected function getComponent($name) protected function getComponent(string $name)
{ {
$formatter = $this->cache->rememberForever('flarum.formatter', function () { if (! static::$formatter) {
return $this->getConfigurator()->finalize(); static::$formatter = $this->cache->rememberForever('flarum.formatter', function () {
}); return $this->finalize();
});
}
return $formatter[$name]; // We will now execute a check on disk, to see whether the requested renderer
// is written to disk. In case cache is not a local file-based driver the below
// `getRenderer()` method won't be able to autoload the file.
if ($name === 'renderer') {
$this->ensureRendererExists();
}
// We will now check revisions and do a sanity check.
if ($this->requiresRefresh()) {
$this->finalize();
}
return Arr::get(static::$formatter, $name);
} }
/** /**
@@ -227,4 +250,61 @@ class Formatter
{ {
return $this->getComponent('js'); return $this->getComponent('js');
} }
protected function ensureRendererExists()
{
if (! static::$formatter) return;
$revision = $this->getRevision();
if (file_exists($this->cacheDir . "/Renderer_$revision.php")) return;
$renderer = Arr::get(static::$formatter, 'renderer');
if (! empty($renderer)) {
file_put_contents($this->cacheDir . "/Renderer_$revision.php", $renderer);
} else {
$this->finalize();
}
// Reload because finalizing might have generated a new one.
$renderer = Arr::get(static::$formatter, 'renderer');
if ($renderer && static::$formatter['renderer'] instanceof __PHP_Incomplete_Class) {
// Autoload the file from disk using a simple include, while suppressing errors.
@include $this->cacheDir . "/Renderer_$revision.php";
// Reload the formatter again from cache to resolve the __PHP_Incomplete_Class
static::$formatter = $this->cache->get('flarum.formatter');
}
}
protected function finalize()
{
$formatter = $this->getConfigurator()->finalize();
preg_match('~^Renderer\_(?<revision>[^\.]+)$~', get_class($formatter['renderer']), $m);
$revision = $m['revision'];
$this->putRevision($revision);
return $formatter;
}
protected function requiresRefresh(): bool
{
if (! $this->getRevision()) return true;
if (! Arr::get(static::$formatter, 'renderer')) return true;
$renderer = static::$formatter['renderer'] instanceof __PHP_Incomplete_Class
? (new ArrayObject(static::$formatter['renderer']))['__PHP_Incomplete_Class_Name']
: get_class(static::$formatter['renderer']);
if (preg_match('~^Renderer_(?<revision>[^\.]+)$~', $renderer, $m)) {
return $this->getRevision() !== $m['revision'];
}
return true;
}
} }

View File

@@ -24,7 +24,8 @@ class FormatterServiceProvider extends AbstractServiceProvider
$this->container->singleton('flarum.formatter', function (Container $container) { $this->container->singleton('flarum.formatter', function (Container $container) {
return new Formatter( return new Formatter(
new Repository($container->make('cache.filestore')), new Repository($container->make('cache.filestore')),
$this->container[Paths::class]->storage.'/formatter' $container[Paths::class]->storage.'/formatter',
$container->make('filesystem')->disk('flarum-assets')
); );
}); });