fipamo/app/Services/Data/ContentService.php
ro cec992ccd4
package updates, first pass at render performance
updated dependencies to the latest versions

also took a swing at rendering performance, which was pretty basic. it
was fine for sites with not that many pages, but it gets a bit stick
above 50

as of right now, performance has been improved slighthy by forcing some
garbage collection, rendering 65 pages in under 30 seconds with 256MB
available.

the issue seems to the speed of initial page collections from the
directories and then converting them to objects that the system can use.
going to explore some options to optimize that particular script

also removed page load from PageRepository instantion because when the
app loads, it was calling that class as part of the start of process and
loading pages everytime no matter what page was being looked at.
removing that made non page sections snappier
2025-06-17 18:24:53 -06:00

187 lines
6.8 KiB
PHP

<?php
namespace App\Services\Data;
use League\CommonMark\MarkdownConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\Attributes\AttributesExtension;
use League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
class ContentService
{
protected $files = [];
protected $config = [];
protected $environment;
protected $converter;
protected $pageCount = "";
public function __construct()
{
//set up conversion handler
$this->environment = new Environment($this->config);
$this->environment->addExtension(new CommonMarkCoreExtension());
// Add the extension
$this->environment->addExtension(new FrontMatterExtension());
//Add Strikethrough rendering
$this->environment->addExtension(new StrikethroughExtension());
//add attributes to elements in markdown
$this->environment->addExtension(new AttributesExtension());
//add table rendering
$this->environment->addExtension(new TableExtension());
// Instantiate the converter engine and start converting some Markdown!
$this->converter = new MarkdownConverter($this->environment);
$this->loadPages(env('PAGES_PATH'));
}
public function loadPages($folder)
{
$folders = glob("$folder/*", GLOB_ONLYDIR);
foreach ($folders as $folder) {
//$this->files[] = $folder . "/";
$this->loadPages($folder);
}
$files = array_filter(glob("$folder/*md"), 'is_file');
foreach ($files as $file) {
$this->files[] = $file;
}
$this->pageCount = count($this->files);
}
public function loadAllPages()
{
$contents = [];
foreach ($this->files as $file) {
//get meta and html from file
$result = $this->converter->convert(file_get_contents($file));
$meta = [];
if ($result instanceof RenderedContentWithFrontMatter) {
$meta = $result->getFrontMatter();
}
//get raw markdown from file
$frontMatterExtension = new FrontMatterExtension();
$parsed = $frontMatterExtension
->getFrontMatterParser()
->parse(file_get_contents($file));
//never trust the front end. clean it up
$soap = (new HtmlSanitizerConfig())
// Allow "safe" elements and attributes. All scripts will be removed
// as well as other dangerous behaviors like CSS injection
->allowSafeElements()
->allowElement('div', ['class', 'title', 'id', 'style'])
->allowElement('img', ['src', 'alt', 'title', 'class'])
->allowElement('iframe', ['height', 'width', 'title', 'src'])
->allowElement('table')
->allowElement('li')
->allowRelativeMedias()
->allowRelativeLinks();
$laundry = new HtmlSanitizer($soap);
$scrubbed = $laundry->sanitize($result->getContent());
unset($result);
if (isset($meta['feature'])) {
$featureList = explode(',', $meta['feature']);
} else {
$featureList = "";
}
$docs = '';
if (isset($meta['files'])) {
$fileList = explode(',', $meta['files']);
$docs = $meta['files'];
} else {
$fileList = [];
$docs = '';
}
$media = [];
$files = [];
if ($featureList != '') {
foreach ($featureList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($media, ['file' => $item, 'type' => trim($ext)]);
}
}
}
if ($fileList != "") {
foreach ($fileList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($files, ['file' => $item, 'type' => trim($ext)]);
}
}
}
//sort attributes into page object
$page = [
'id' => $meta['id'],
'uuid' => $meta['uuid'],
'title' => $meta['title'],
'feature' => $meta['feature'],
'files' => $docs,
'path' => $meta['path'],
'layout' => $meta['layout'],
'tags' => $meta['tags'],
'author' => $meta['author'],
'created' => date('Y M D d', $meta['created']),
'updated' => date('Y M D d', $meta['updated']),
'rawCreated' => $meta['created'],
'rawUpdated' => $meta['updated'],
'createdYear' => date('Y', $meta['created']),
'createdMonth' => date('m', $meta['created']),
'deleted' => $meta['deleted'],
'menu' => $meta['menu'],
'featured' => $meta['featured'],
'published' => $meta['published'],
'slug' => $meta['slug'],
'filePath' => $file,
'content' => $parsed->getContent(),
'html' => $scrubbed,
'media' => $media,
'docs' => $files
];
//checks for duplicates
$uuid = $meta['uuid'];
$found = current(
array_filter($contents, function ($item) use ($uuid) {
return isset($item['uuid']) && $uuid == $item['uuid'];
})
);
// if uuid is not present, add it
if (!$found) {
array_push($contents, $page);
}
unset($file);
gc_collect_cycles();
}
$collection = collect($contents);
$sorted = $collection->sortBy([
['id', 'desc'],
]);
$sorted->values()->all();
return $sorted;
}
public static function getAll()
{
echo("YES");
}
}