104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeau 404fd306cSNickeaunamespace ComboStrap; 504fd306cSNickeau 604fd306cSNickeau 704fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore; 804fd306cSNickeauuse ComboStrap\Web\Url; 904fd306cSNickeauuse ComboStrap\Web\UrlEndpoint; 1004fd306cSNickeauuse ComboStrap\Xml\XmlDocument; 1104fd306cSNickeauuse Doku_Renderer_metadata; 1204fd306cSNickeauuse dokuwiki\Cache\CacheInstructions; 1304fd306cSNickeauuse dokuwiki\Cache\CacheParser; 1404fd306cSNickeauuse dokuwiki\Cache\CacheRenderer; 1504fd306cSNickeauuse Exception; 1604fd306cSNickeau 1704fd306cSNickeau 1804fd306cSNickeau/** 1904fd306cSNickeau * A class that renders a markup fragment 2004fd306cSNickeau * This is the context object 2104fd306cSNickeau * during parsing and rendering is determined by {@link FetcherMarkup} 2204fd306cSNickeau * 2304fd306cSNickeau * You can get it in any place via {@link ExecutionContext::getExecutingMarkupHandler()} 2404fd306cSNickeau * 2504fd306cSNickeau * It: 2604fd306cSNickeau * * does not output any full page (HTML document) but only fragment. 2704fd306cSNickeau * * manage the dependencies (snippets, cache) 2804fd306cSNickeau * 2904fd306cSNickeau * This is not really a {@link IFetcher function} because it should not be called 3004fd306cSNickeau * from the outside but to be able to use the {@link FetcherCache} we need to. 3104fd306cSNickeau * (as fetcher cache uses the url as unique identifier) 3204fd306cSNickeau * 3304fd306cSNickeau * 3404fd306cSNickeau * TODO: {@link MarkupRenderer} could be one with {@link FetcherMarkup} ? 3504fd306cSNickeau * 3604fd306cSNickeau * Not all properties are public to support 3704fd306cSNickeau * the {@link FetcherMarkupBuilder} pattern. 3804fd306cSNickeau * Php does not support internal class and protected does not 3904fd306cSNickeau * work for class on the same namespace. 4004fd306cSNickeau */ 4104fd306cSNickeauclass FetcherMarkup extends IFetcherAbs implements IFetcherSource, IFetcherString 4204fd306cSNickeau{ 4304fd306cSNickeau 4404fd306cSNickeau 4504fd306cSNickeau const XHTML_MODE = "xhtml"; 4604fd306cSNickeau const MAX_CACHE_AGE = 999999; 4704fd306cSNickeau 4804fd306cSNickeau const CANONICAL = "markup-fragment-fetcher"; 4904fd306cSNickeau 5004fd306cSNickeau /** 5104fd306cSNickeau * When the rendering is done from: 5204fd306cSNickeau * * a string 5304fd306cSNickeau * * or an instructions (template) 5404fd306cSNickeau * but not from a file 5504fd306cSNickeau */ 5604fd306cSNickeau public const MARKUP_DYNAMIC_EXECUTION_NAME = "markup-dynamic-execution"; 5704fd306cSNickeau 5804fd306cSNickeau /** 5904fd306cSNickeau * @var array - toc in a dokuwiki format 6004fd306cSNickeau */ 6104fd306cSNickeau public array $toc; 6204fd306cSNickeau 6304fd306cSNickeau /** 6404fd306cSNickeau * @var CacheParser cache file (may be not set if this is not a {@link self::isPathExecution() execution} 6504fd306cSNickeau */ 6604fd306cSNickeau public CacheParser $contentCache; 6704fd306cSNickeau 6804fd306cSNickeau /** 6904fd306cSNickeau * @var string the type of object (known as renderer in Dokuwiki) 7004fd306cSNickeau */ 7104fd306cSNickeau public string $builderName; 7204fd306cSNickeau 7304fd306cSNickeau public array $requestedInstructions; 7404fd306cSNickeau public array $contextData; 7504fd306cSNickeau 7604fd306cSNickeau public CacheInstructions $instructionsCache; 7704fd306cSNickeau 7804fd306cSNickeau /** 7904fd306cSNickeau * @var CacheRenderer This cache file stores the last render timestamp (see {@link p_get_metadata()} 8004fd306cSNickeau */ 8104fd306cSNickeau public CacheRenderer $metaCache; 8204fd306cSNickeau public LocalPath $metaPath; 8304fd306cSNickeau 8404fd306cSNickeau /** 8504fd306cSNickeau * @var CacheParser 8604fd306cSNickeau */ 8704fd306cSNickeau public CacheParser $snippetCache; 8804fd306cSNickeau 8904fd306cSNickeau /** 9004fd306cSNickeau * @var FetcherMarkup - the parent (a instructions run may run inside a path run, ie {@link \syntax_plugin_combo_iterator) 9104fd306cSNickeau */ 9204fd306cSNickeau public FetcherMarkup $parentMarkupHandler; 9304fd306cSNickeau 9404fd306cSNickeau /** 9504fd306cSNickeau * @var bool threat the markup as a document (not as a fragment) 9604fd306cSNickeau */ 9704fd306cSNickeau public bool $isDoc; 9804fd306cSNickeau 9904fd306cSNickeau 10004fd306cSNickeau public Mime $mime; 10104fd306cSNickeau private bool $cacheAfterRendering = true; 10204fd306cSNickeau public MarkupCacheDependencies $outputCacheDependencies; 10304fd306cSNickeau 10404fd306cSNickeau 10504fd306cSNickeau /** 10604fd306cSNickeau * @var Snippet[] 10704fd306cSNickeau */ 10804fd306cSNickeau private array $localSnippets = []; 10904fd306cSNickeau 11004fd306cSNickeau public bool $deleteRootBlockElement = false; 11104fd306cSNickeau 11204fd306cSNickeau /** 11304fd306cSNickeau * @var WikiPath the context path, it's important to resolve relative link and to create cache for each context namespace for instance 11404fd306cSNickeau */ 11504fd306cSNickeau public WikiPath $requestedContextPath; 11604fd306cSNickeau 11704fd306cSNickeau /** 11804fd306cSNickeau * @var Path the source path of the markup (may be not set if we render a markup string for instance) 11904fd306cSNickeau */ 12004fd306cSNickeau public Path $markupSourcePath; 12104fd306cSNickeau 12204fd306cSNickeau 12304fd306cSNickeau public string $markupString; 12404fd306cSNickeau 12504fd306cSNickeau /** 12604fd306cSNickeau * @var bool true if this fetcher has already run 12704fd306cSNickeau * ( 12804fd306cSNickeau * Fighting file modified time, even if we cache has been stored, 12904fd306cSNickeau * the modified time is not always good, this indicator will 13004fd306cSNickeau * make the processing not run twice) 13104fd306cSNickeau */ 13204fd306cSNickeau private bool $hasExecuted = false; 13304fd306cSNickeau 13404fd306cSNickeau /** 13504fd306cSNickeau * The result 13604fd306cSNickeau * @var string 13704fd306cSNickeau */ 13804fd306cSNickeau private string $fetchString; 13904fd306cSNickeau 14004fd306cSNickeau 14104fd306cSNickeau /** 14204fd306cSNickeau * @var array 14304fd306cSNickeau */ 14404fd306cSNickeau private array $meta; 14504fd306cSNickeau 14604fd306cSNickeau /** 14704fd306cSNickeau * @var bool - when a execution is not a {@link self::isPathExecution()}, the snippet will not be stored automatically. 14804fd306cSNickeau * To avoid this problem, a warning is send if the calling code does not set explicitly that this is specifically a 14904fd306cSNickeau * standalone execution 15004fd306cSNickeau */ 15104fd306cSNickeau public bool $isNonPathStandaloneExecution = false; 15204fd306cSNickeau /** 15304fd306cSNickeau * @var array 15404fd306cSNickeau */ 15504fd306cSNickeau private array $processedInstructions; 15604fd306cSNickeau 15704fd306cSNickeau 15804fd306cSNickeau /** 15904fd306cSNickeau * @param Path $executingPath - the path where we can find the markup 16004fd306cSNickeau * @param ?WikiPath $contextPath - the context path, the requested path in the browser url (from where relative component are resolved (ie links, ...)) 16104fd306cSNickeau * @return FetcherMarkup 16204fd306cSNickeau * @throws ExceptionNotExists 16304fd306cSNickeau */ 16404fd306cSNickeau public static function createXhtmlMarkupFetcherFromPath(Path $executingPath, WikiPath $contextPath = null): FetcherMarkup 16504fd306cSNickeau { 16604fd306cSNickeau if ($contextPath === null) { 16704fd306cSNickeau try { 16804fd306cSNickeau $contextPath = $executingPath->toWikiPath(); 16904fd306cSNickeau } catch (ExceptionCast $e) { 17004fd306cSNickeau /** 17104fd306cSNickeau * Not a wiki path, default to the default 17204fd306cSNickeau */ 17304fd306cSNickeau $contextPath = ExecutionContext::getActualOrCreateFromEnv()->getDefaultContextPath(); 17404fd306cSNickeau } 17504fd306cSNickeau } 17604fd306cSNickeau return FetcherMarkup::confRoot() 17704fd306cSNickeau ->setRequestedExecutingPath($executingPath) 17804fd306cSNickeau ->setRequestedContextPath($contextPath) 17904fd306cSNickeau ->setRequestedMimeToXhtml() 18004fd306cSNickeau ->build(); 18104fd306cSNickeau } 18204fd306cSNickeau 18304fd306cSNickeau 18404fd306cSNickeau public static function confRoot(): FetcherMarkupBuilder 18504fd306cSNickeau { 18604fd306cSNickeau return new FetcherMarkupBuilder(); 18704fd306cSNickeau } 18804fd306cSNickeau 18904fd306cSNickeau /** 19004fd306cSNickeau * Use mostly in test 19104fd306cSNickeau * The coutnerpart of {@link \ComboStrap\Test\TestUtility::renderText2XhtmlWithoutP()} 19204fd306cSNickeau * @throws ExceptionNotExists 19304fd306cSNickeau */ 19404fd306cSNickeau public static function createStandaloneExecutionFromStringMarkupToXhtml(string $markup): FetcherMarkup 19504fd306cSNickeau { 19604fd306cSNickeau return self::confRoot() 19704fd306cSNickeau ->setRequestedMarkupString($markup) 19804fd306cSNickeau ->setDeleteRootBlockElement(true) 19904fd306cSNickeau ->setRequestedContextPathWithDefault() 20004fd306cSNickeau ->setRequestedMimeToXhtml() 20104fd306cSNickeau ->setIsStandAloneCodeExecution(true) 20204fd306cSNickeau ->build(); 20304fd306cSNickeau } 20404fd306cSNickeau 20504fd306cSNickeau /** 20604fd306cSNickeau * Starts a child fetcher markup 20704fd306cSNickeau * This is needed for instructions or markup run 20804fd306cSNickeau * Why ? Because the snippets advertised during this run, need to be stored 20904fd306cSNickeau * and we need to know the original request path that is in the parent run. 21004fd306cSNickeau * @return FetcherMarkupBuilder 21104fd306cSNickeau */ 21204fd306cSNickeau public static function confChild(): FetcherMarkupBuilder 21304fd306cSNickeau { 21404fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 21504fd306cSNickeau try { 21604fd306cSNickeau $executing = $executionContext->getExecutingMarkupHandler(); 21704fd306cSNickeau } catch (ExceptionNotFound $e) { 21804fd306cSNickeau if (PluginUtility::isDevOrTest() && $executionContext->getExecutingAction() !== ExecutionContext::PREVIEW_ACTION) { 21904fd306cSNickeau LogUtility::warning("A markup handler is not running, we couldn't create a child."); 22004fd306cSNickeau } 221d860c427Sgerardnico $contextPath = $executionContext->getContextPath(); 222d860c427Sgerardnico return self::confRoot() 223d860c427Sgerardnico ->setRequestedContextPath($contextPath); 22404fd306cSNickeau } 22504fd306cSNickeau return self::confRoot() 22604fd306cSNickeau ->setParentMarkupHandler($executing) 22704fd306cSNickeau ->setRequestedContextPath($executing->getRequestedContextPath()); 22804fd306cSNickeau } 22904fd306cSNickeau 23004fd306cSNickeau 23104fd306cSNickeau /** 23204fd306cSNickeau * Dokuwiki will wrap the markup in a p element 23304fd306cSNickeau * if the first element is not a block 23404fd306cSNickeau * This option permits to delete it. This is used mostly in test to get 23504fd306cSNickeau * the generated html 23604fd306cSNickeau */ 23704fd306cSNickeau public function deleteRootPElementsIfRequested(array &$instructions): void 23804fd306cSNickeau { 23904fd306cSNickeau 24004fd306cSNickeau if (!$this->deleteRootBlockElement) { 24104fd306cSNickeau return; 24204fd306cSNickeau } 24304fd306cSNickeau 24404fd306cSNickeau /** 24504fd306cSNickeau * Delete the p added by {@link Block::process()} 24604fd306cSNickeau * if the plugin of the {@link SyntaxPlugin::getPType() normal} and not in a block 24704fd306cSNickeau * 24804fd306cSNickeau * p_open = document_start in renderer 24904fd306cSNickeau */ 25004fd306cSNickeau if ($instructions[1][0] !== 'p_open') { 25104fd306cSNickeau return; 25204fd306cSNickeau } 25304fd306cSNickeau unset($instructions[1]); 25404fd306cSNickeau 25504fd306cSNickeau /** 25604fd306cSNickeau * The last p position is not fix 25704fd306cSNickeau * We may have other calls due for instance 25804fd306cSNickeau * of {@link \action_plugin_combo_syntaxanalytics} 25904fd306cSNickeau */ 26004fd306cSNickeau $n = 1; 26104fd306cSNickeau while (($lastPBlockPosition = (sizeof($instructions) - $n)) >= 0) { 26204fd306cSNickeau 26304fd306cSNickeau /** 26404fd306cSNickeau * p_open = document_end in renderer 26504fd306cSNickeau */ 26604fd306cSNickeau if ($instructions[$lastPBlockPosition][0] == 'p_close') { 26704fd306cSNickeau unset($instructions[$lastPBlockPosition]); 26804fd306cSNickeau break; 26904fd306cSNickeau } else { 27004fd306cSNickeau $n = $n + 1; 27104fd306cSNickeau } 27204fd306cSNickeau } 27304fd306cSNickeau 27404fd306cSNickeau } 27504fd306cSNickeau 27604fd306cSNickeau /** 27704fd306cSNickeau * 27804fd306cSNickeau * @param Url|null $url 27904fd306cSNickeau * @return Url 28004fd306cSNickeau * 28104fd306cSNickeau * Note: The fetch url is the {@link FetcherCache keyCache} 28204fd306cSNickeau */ 28304fd306cSNickeau function getFetchUrl(Url $url = null): Url 28404fd306cSNickeau { 28504fd306cSNickeau /** 28604fd306cSNickeau * Overwrite default fetcher endpoint 28704fd306cSNickeau * that is {@link UrlEndpoint::createFetchUrl()} 28804fd306cSNickeau */ 28904fd306cSNickeau $url = UrlEndpoint::createDokuUrl(); 29004fd306cSNickeau $url = parent::getFetchUrl($url); 29104fd306cSNickeau try { 29204fd306cSNickeau $wikiPath = $this->getSourcePath()->toWikiPath(); 29304fd306cSNickeau $url->addQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $wikiPath->getWikiId()); 29404fd306cSNickeau $url->addQueryParameter(WikiPath::DRIVE_ATTRIBUTE, $wikiPath->getDrive()); 29504fd306cSNickeau } catch (ExceptionCast|ExceptionNotFound $e) { 29604fd306cSNickeau // not an accessible source path 29704fd306cSNickeau } 29804fd306cSNickeau $url->addQueryParameter("context-id", $this->getRequestedContextPath()->getWikiId()); 29904fd306cSNickeau return $url; 30004fd306cSNickeau 30104fd306cSNickeau } 30204fd306cSNickeau 30304fd306cSNickeau 30404fd306cSNickeau /** 30504fd306cSNickeau * @return Mime 30604fd306cSNickeau */ 30704fd306cSNickeau public function getMime(): Mime 30804fd306cSNickeau { 30904fd306cSNickeau if (isset($this->mime)) { 31004fd306cSNickeau return $this->mime; 31104fd306cSNickeau } 31204fd306cSNickeau 31304fd306cSNickeau // XHTML default 31404fd306cSNickeau try { 31504fd306cSNickeau return Mime::createFromExtension(self::XHTML_MODE); 31604fd306cSNickeau } catch (ExceptionNotFound $e) { 31704fd306cSNickeau // should not happen 31804fd306cSNickeau throw new ExceptionRuntime("Internal error: The XHTML mime was not found.", self::CANONICAL, 1, $e); 31904fd306cSNickeau } 32004fd306cSNickeau } 32104fd306cSNickeau 32204fd306cSNickeau /** 32304fd306cSNickeau * TODO: split We should split fetcherMarkup by object type output and {@link Mime} 32404fd306cSNickeau * @return bool 32504fd306cSNickeau */ 32604fd306cSNickeau private function shouldInstructionProcess(): bool 32704fd306cSNickeau { 32804fd306cSNickeau 32904fd306cSNickeau if (!$this->isPathExecution()) { 33004fd306cSNickeau return true; 33104fd306cSNickeau } 33204fd306cSNickeau 33304fd306cSNickeau if (isset($this->processedInstructions)) { 33404fd306cSNickeau return false; 33504fd306cSNickeau } 33604fd306cSNickeau 33704fd306cSNickeau /** 33804fd306cSNickeau * Edge Case 33904fd306cSNickeau * (as dokuwiki starts the rendering process here 34004fd306cSNickeau * we need to set the execution id) 34104fd306cSNickeau */ 34204fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv()->setExecutingMarkupHandler($this); 34304fd306cSNickeau try { 34404fd306cSNickeau $useCache = $this->instructionsCache->useCache(); 34504fd306cSNickeau } finally { 34604fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 34704fd306cSNickeau } 34804fd306cSNickeau return ($useCache === false); 34904fd306cSNickeau } 35004fd306cSNickeau 35104fd306cSNickeau public function shouldProcess(): bool 35204fd306cSNickeau { 35304fd306cSNickeau 35404fd306cSNickeau if (!$this->isPathExecution()) { 35504fd306cSNickeau return true; 35604fd306cSNickeau } 35704fd306cSNickeau 35804fd306cSNickeau if ($this->hasExecuted) { 35904fd306cSNickeau return false; 36004fd306cSNickeau } 36104fd306cSNickeau 36204fd306cSNickeau /** 36304fd306cSNickeau * The cache is stored by requested page scope 36404fd306cSNickeau * 36504fd306cSNickeau * We set the environment because 36604fd306cSNickeau * {@link CacheParser::useCache()} may call a parsing of the markup fragment 36704fd306cSNickeau * And the global environment are not always passed 36804fd306cSNickeau * in all actions and is needed to log the {@link CacheResult cache 36904fd306cSNickeau * result} 37004fd306cSNickeau * 37104fd306cSNickeau * Use cache should be always called because it trigger 37204fd306cSNickeau * the event coupled to the cache (ie PARSER_CACHE_USE) 37304fd306cSNickeau */ 37404fd306cSNickeau $depends['age'] = $this->getCacheAge(); 37504fd306cSNickeau if ($this->isFragment()) { 37604fd306cSNickeau /** 37704fd306cSNickeau * Fragment may use variables of the requested page 37804fd306cSNickeau * We have dependency on {@link MarkupCacheDependencies::PAGE_PRIMARY_META_DEPENDENCY} 37904fd306cSNickeau * but as they may be derived such as the {@link PageTitle} 38004fd306cSNickeau * comes from the H1 or the feature image comes from the first image in the section 1 38104fd306cSNickeau * We can't really use this event. 38204fd306cSNickeau */ 38304fd306cSNickeau try { 38404fd306cSNickeau $depends['files'][] = FetcherMarkup::confRoot() 38504fd306cSNickeau ->setRequestedContextPath($this->getRequestedContextPath()) 38604fd306cSNickeau ->setRequestedExecutingPath($this->getRequestedContextPath()) 38704fd306cSNickeau ->setRequestedMimeToMetadata() 38804fd306cSNickeau ->build() 38904fd306cSNickeau ->getMetadataPath() 39004fd306cSNickeau ->toAbsoluteId(); 39104fd306cSNickeau } catch (ExceptionNotExists|ExceptionNotFound $e) { 39204fd306cSNickeau /** 39304fd306cSNickeau * Computer are hard 39404fd306cSNickeau * At the beginning there is no markup path 39504fd306cSNickeau * We may get this error then 39604fd306cSNickeau * 39704fd306cSNickeau * We don't allow on test 39804fd306cSNickeau */ 39904fd306cSNickeau if (PluginUtility::isTest()) { 40004fd306cSNickeau /** 40104fd306cSNickeau * The first edit, the page does not exists 40204fd306cSNickeau */ 40304fd306cSNickeau $executingAction = ExecutionContext::getActualOrCreateFromEnv()->getExecutingAction(); 40404fd306cSNickeau if (!in_array($executingAction, [ExecutionContext::EDIT_ACTION, ExecutionContext::PREVIEW_ACTION])) { 40504fd306cSNickeau LogUtility::error("The metadata path should be known. " . $e->getMessage(), self::CANONICAL, $e); 40604fd306cSNickeau } 40704fd306cSNickeau } 40804fd306cSNickeau } 40904fd306cSNickeau } 41004fd306cSNickeau /** 41104fd306cSNickeau * Edge Case 41204fd306cSNickeau * (as dokuwiki starts the rendering process here 41304fd306cSNickeau * we need to set the execution id) 41404fd306cSNickeau */ 41504fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv() 41604fd306cSNickeau ->setExecutingMarkupHandler($this); 41704fd306cSNickeau try { 41804fd306cSNickeau $useCache = $this->contentCache->useCache($depends); 41904fd306cSNickeau } finally { 42004fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 42104fd306cSNickeau } 42204fd306cSNickeau return ($useCache === false); 42304fd306cSNickeau 42404fd306cSNickeau } 42504fd306cSNickeau 42604fd306cSNickeau 42704fd306cSNickeau public 42804fd306cSNickeau function storeSnippets() 42904fd306cSNickeau { 43004fd306cSNickeau 43104fd306cSNickeau /** 43204fd306cSNickeau * Snippet 43304fd306cSNickeau */ 43404fd306cSNickeau $snippets = $this->getSnippets(); 43504fd306cSNickeau $jsonDecodeSnippets = SnippetSystem::toJsonArrayFromSlotSnippets($snippets); 43604fd306cSNickeau 43704fd306cSNickeau /** 43804fd306cSNickeau * Cache file 43904fd306cSNickeau * Using a cache parser, set the page id and will trigger 44004fd306cSNickeau * the parser cache use event in order to log/report the cache usage 44104fd306cSNickeau * At {@link action_plugin_combo_cache::createCacheReport()} 44204fd306cSNickeau */ 44304fd306cSNickeau $snippetCache = $this->getSnippetCacheStore(); 44404fd306cSNickeau $this->outputCacheDependencies->rerouteCacheDestination($snippetCache); 44504fd306cSNickeau 44604fd306cSNickeau if (count($jsonDecodeSnippets) > 0) { 44704fd306cSNickeau $data1 = json_encode($jsonDecodeSnippets); 44804fd306cSNickeau $snippetCache->storeCache($data1); 44904fd306cSNickeau } else { 45004fd306cSNickeau $snippetCache->removeCache(); 45104fd306cSNickeau } 45204fd306cSNickeau 45304fd306cSNickeau } 45404fd306cSNickeau 45504fd306cSNickeau /** 45604fd306cSNickeau * This functon loads the snippets in the global array 45704fd306cSNickeau * by creating them. Not ideal but works for now. 45804fd306cSNickeau * @return Snippet[] 45904fd306cSNickeau */ 46004fd306cSNickeau public 46104fd306cSNickeau function loadSnippets(): array 46204fd306cSNickeau { 46304fd306cSNickeau 46404fd306cSNickeau $snippetCacheStore = $this->getSnippetCacheStore(); 46504fd306cSNickeau $data = $snippetCacheStore->retrieveCache(); 46604fd306cSNickeau $nativeSnippets = []; 46704fd306cSNickeau if (!empty($data)) { 46804fd306cSNickeau $jsonDecodeSnippets = json_decode($data, true); 46904fd306cSNickeau foreach ($jsonDecodeSnippets as $snippet) { 47004fd306cSNickeau try { 47104fd306cSNickeau $nativeSnippets[] = Snippet::createFromJson($snippet); 47204fd306cSNickeau } catch (ExceptionCompile $e) { 47304fd306cSNickeau LogUtility::error("The snippet json array cannot be build into a snippet object. " . $e->getMessage() . "\n" . ArrayUtility::formatAsString($snippet), LogUtility::SUPPORT_CANONICAL,); 47404fd306cSNickeau } 47504fd306cSNickeau } 47604fd306cSNickeau } 47704fd306cSNickeau return $nativeSnippets; 47804fd306cSNickeau 47904fd306cSNickeau } 48004fd306cSNickeau 48104fd306cSNickeau private 48204fd306cSNickeau function removeSnippets() 48304fd306cSNickeau { 48404fd306cSNickeau $snippetCacheFile = $this->getSnippetCacheStore()->cache; 48504fd306cSNickeau if ($snippetCacheFile !== null) { 48604fd306cSNickeau if (file_exists($snippetCacheFile)) { 48704fd306cSNickeau unlink($snippetCacheFile); 48804fd306cSNickeau } 48904fd306cSNickeau } 49004fd306cSNickeau } 49104fd306cSNickeau 49204fd306cSNickeau /** 49304fd306cSNickeau * @return CacheParser - the cache where the snippets are stored 49404fd306cSNickeau * Cache file 49504fd306cSNickeau * Using a cache parser, set the page id and will trigger 49604fd306cSNickeau * the parser cache use event in order to log/report the cache usage 49704fd306cSNickeau * At {@link action_plugin_combo_cache::createCacheReport()} 49804fd306cSNickeau */ 49904fd306cSNickeau public 50004fd306cSNickeau function getSnippetCacheStore(): CacheParser 50104fd306cSNickeau { 50204fd306cSNickeau if (isset($this->snippetCache)) { 50304fd306cSNickeau return $this->snippetCache; 50404fd306cSNickeau } 50504fd306cSNickeau if ($this->isPathExecution()) { 50604fd306cSNickeau throw new ExceptionRuntimeInternal("A source path should be available as this is a path execution"); 50704fd306cSNickeau } 50804fd306cSNickeau throw new ExceptionRuntime("There is no snippet cache store for a non-path execution"); 50904fd306cSNickeau 51004fd306cSNickeau } 51104fd306cSNickeau 51204fd306cSNickeau 51304fd306cSNickeau public 51404fd306cSNickeau function getDependenciesCacheStore(): CacheParser 51504fd306cSNickeau { 51604fd306cSNickeau return $this->outputCacheDependencies->getDependenciesCacheStore(); 51704fd306cSNickeau } 51804fd306cSNickeau 51904fd306cSNickeau public 52004fd306cSNickeau function getDependenciesCachePath(): LocalPath 52104fd306cSNickeau { 52204fd306cSNickeau $cachePath = $this->outputCacheDependencies->getDependenciesCacheStore()->cache; 52304fd306cSNickeau return LocalPath::createFromPathString($cachePath); 52404fd306cSNickeau } 52504fd306cSNickeau 52604fd306cSNickeau /** 52704fd306cSNickeau * @return LocalPath the fetch path - start the process and returns a path. If the cache is on, return the {@link FetcherMarkup::getContentCachePath()} 52804fd306cSNickeau * @throws ExceptionCompile 52904fd306cSNickeau */ 53004fd306cSNickeau function processIfNeededAndGetFetchPath(): LocalPath 53104fd306cSNickeau { 53204fd306cSNickeau $this->processIfNeeded(); 53304fd306cSNickeau 53404fd306cSNickeau /** 53504fd306cSNickeau * The cache path may have change due to the cache key rerouting 53604fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 53704fd306cSNickeau * as fetch path 53804fd306cSNickeau */ 53904fd306cSNickeau return $this->getContentCachePath(); 54004fd306cSNickeau 54104fd306cSNickeau } 54204fd306cSNickeau 54304fd306cSNickeau 54404fd306cSNickeau /** 54504fd306cSNickeau * @return $this 54604fd306cSNickeau * @throws ExceptionCompile 54704fd306cSNickeau */ 54804fd306cSNickeau public function process(): FetcherMarkup 54904fd306cSNickeau { 55004fd306cSNickeau 55104fd306cSNickeau $this->hasExecuted = true; 55204fd306cSNickeau 55304fd306cSNickeau /** 55404fd306cSNickeau * Rendering 55504fd306cSNickeau */ 55604fd306cSNickeau $executionContext = (ExecutionContext::getActualOrCreateFromEnv()); 55704fd306cSNickeau 55804fd306cSNickeau $extension = $this->getMime()->getExtension(); 55904fd306cSNickeau switch ($extension) { 56004fd306cSNickeau 56104fd306cSNickeau case MarkupRenderer::METADATA_EXTENSION: 56204fd306cSNickeau /** 56304fd306cSNickeau * The user may ask just for the metadata 56404fd306cSNickeau * and should then use the {@link self::getMetadata()} 56504fd306cSNickeau * function instead 56604fd306cSNickeau */ 56704fd306cSNickeau break; 56804fd306cSNickeau case MarkupRenderer::INSTRUCTION_EXTENSION: 56904fd306cSNickeau /** 57004fd306cSNickeau * The user may ask just for the instuctions 57104fd306cSNickeau * and should then use the {@link self::getInstructions()} 57204fd306cSNickeau * function to get the instructions 57304fd306cSNickeau */ 57404fd306cSNickeau return $this; 57504fd306cSNickeau default: 57604fd306cSNickeau 57704fd306cSNickeau $instructions = $this->getInstructions(); 57804fd306cSNickeau 57904fd306cSNickeau /** 58004fd306cSNickeau * Edge case: We delete here 58104fd306cSNickeau * because the instructions may have been created by dokuwiki 58204fd306cSNickeau * when we test for the cache with {@link CacheParser::useCache()} 58304fd306cSNickeau */ 58404fd306cSNickeau if ($this->deleteRootBlockElement) { 58504fd306cSNickeau self::deleteRootPElementsIfRequested($instructions); 58604fd306cSNickeau } 58704fd306cSNickeau 58804fd306cSNickeau if (!isset($this->builderName)) { 58904fd306cSNickeau $this->builderName = $this->getMime()->getExtension(); 59004fd306cSNickeau } 59104fd306cSNickeau 59204fd306cSNickeau $executionContext->setExecutingMarkupHandler($this); 59304fd306cSNickeau try { 59404fd306cSNickeau if ($this->isDocument()) { 59504fd306cSNickeau $markupRenderer = MarkupRenderer::createFromMarkupInstructions($instructions, $this) 59604fd306cSNickeau ->setRequestedMime($this->getMime()) 59704fd306cSNickeau ->setRendererName($this->builderName); 59804fd306cSNickeau 59904fd306cSNickeau $output = $markupRenderer->getOutput(); 60004fd306cSNickeau if ($output === null && !empty($instructions)) { 60104fd306cSNickeau LogUtility::error("The renderer ({$this->builderName}) seems to have been not found"); 60204fd306cSNickeau } 60304fd306cSNickeau $this->cacheAfterRendering = $markupRenderer->getCacheAfterRendering(); 60404fd306cSNickeau } else { 60504fd306cSNickeau $output = MarkupDynamicRender::create($this->builderName)->processInstructions($instructions); 60604fd306cSNickeau } 60704fd306cSNickeau } catch (\Exception $e) { 60804fd306cSNickeau /** 60904fd306cSNickeau * Example of errors; 61004fd306cSNickeau * method_exists() expects parameter 2 to be string, array given 61104fd306cSNickeau * inc\parserutils.php:672 61204fd306cSNickeau */ 61304fd306cSNickeau throw new ExceptionCompile("An error has occurred while getting the output. Error: {$e->getMessage()}", self::CANONICAL, 1, $e); 61404fd306cSNickeau 61504fd306cSNickeau } finally { 61604fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 61704fd306cSNickeau } 61804fd306cSNickeau if (is_array($output)) { 61904fd306cSNickeau LogUtility::internalError("The output was an array", self::CANONICAL); 62004fd306cSNickeau $this->fetchString = serialize($output); 62104fd306cSNickeau } else { 62204fd306cSNickeau $this->fetchString = $output; 62304fd306cSNickeau } 62404fd306cSNickeau 62504fd306cSNickeau break; 62604fd306cSNickeau } 62704fd306cSNickeau 62804fd306cSNickeau /** 62904fd306cSNickeau * Storage of snippets or dependencies 63004fd306cSNickeau * none if this is not a path execution 63104fd306cSNickeau * and for now, metadata storage is done by dokuwiki 63204fd306cSNickeau */ 63304fd306cSNickeau if (!$this->isPathExecution() || $this->mime->getExtension() === MarkupRenderer::METADATA_EXTENSION) { 63404fd306cSNickeau return $this; 63504fd306cSNickeau } 63604fd306cSNickeau 63704fd306cSNickeau 63804fd306cSNickeau /** 63904fd306cSNickeau * Snippets and cache dependencies are only for HTML rendering 64004fd306cSNickeau * Otherwise, otherwise other type rendering may override them 64104fd306cSNickeau * (such as analtyical json, ...) 64204fd306cSNickeau */ 64304fd306cSNickeau if (in_array($this->getMime()->toString(), [Mime::XHTML, Mime::HTML])) { 64404fd306cSNickeau 64504fd306cSNickeau /** 64604fd306cSNickeau * We make the Snippet store to Html store an atomic operation 64704fd306cSNickeau * 64804fd306cSNickeau * Why ? Because if the rendering of the page is stopped, 64904fd306cSNickeau * the cache of the HTML page may be stored but not the cache of the snippets 65004fd306cSNickeau * leading to a bad page because the next rendering will see then no snippets. 65104fd306cSNickeau */ 65204fd306cSNickeau try { 65304fd306cSNickeau $this->storeSnippets(); 65404fd306cSNickeau } catch (Exception $e) { 65504fd306cSNickeau // if any write os exception 65604fd306cSNickeau LogUtility::msg("Error while storing the xhtml content: {$e->getMessage()}"); 65704fd306cSNickeau $this->removeSnippets(); 65804fd306cSNickeau } 65904fd306cSNickeau 66004fd306cSNickeau /** 66104fd306cSNickeau * Cache output dependencies 66204fd306cSNickeau * Reroute the cache output by runtime dependencies 66304fd306cSNickeau * set during processing 66404fd306cSNickeau */ 66504fd306cSNickeau $this->outputCacheDependencies->storeDependencies(); 66604fd306cSNickeau $this->outputCacheDependencies->rerouteCacheDestination($this->contentCache); 66704fd306cSNickeau 66804fd306cSNickeau } 66904fd306cSNickeau 67004fd306cSNickeau /** 67104fd306cSNickeau * We store always the output in the cache 67204fd306cSNickeau * if the cache is not on, the file is just overwritten 67304fd306cSNickeau * 67404fd306cSNickeau * We don't use 67504fd306cSNickeau * {{@link CacheParser::storeCache()} 67604fd306cSNickeau * because it uses the protected parameter `__nocache` 67704fd306cSNickeau * that will disallow the storage 67804fd306cSNickeau */ 67904fd306cSNickeau io_saveFile($this->contentCache->cache, $this->fetchString); 68004fd306cSNickeau 68104fd306cSNickeau return $this; 68204fd306cSNickeau } 68304fd306cSNickeau 68404fd306cSNickeau 68504fd306cSNickeau function getBuster(): string 68604fd306cSNickeau { 68704fd306cSNickeau // no buster 68804fd306cSNickeau return ""; 68904fd306cSNickeau } 69004fd306cSNickeau 69104fd306cSNickeau public 69204fd306cSNickeau function getFetcherName(): string 69304fd306cSNickeau { 69404fd306cSNickeau return "markup-fetcher"; 69504fd306cSNickeau } 69604fd306cSNickeau 69704fd306cSNickeau 69804fd306cSNickeau private function getCacheAge(): int 69904fd306cSNickeau { 70004fd306cSNickeau 70104fd306cSNickeau $extension = $this->getMime()->getExtension(); 70204fd306cSNickeau switch ($extension) { 70304fd306cSNickeau case self::XHTML_MODE: 70404fd306cSNickeau if (!Site::isHtmlRenderCacheOn()) { 70504fd306cSNickeau return 0; 70604fd306cSNickeau } 70704fd306cSNickeau break; 70804fd306cSNickeau case MarkupRenderer::INSTRUCTION_EXTENSION: 70904fd306cSNickeau // indefinitely 71004fd306cSNickeau return self::MAX_CACHE_AGE; 71104fd306cSNickeau } 71204fd306cSNickeau try { 71304fd306cSNickeau $requestedCache = $this->getRequestedCache(); 71404fd306cSNickeau } catch (ExceptionNotFound $e) { 71504fd306cSNickeau $requestedCache = IFetcherAbs::RECACHE_VALUE; 71604fd306cSNickeau } 71704fd306cSNickeau $cacheAge = $this->getCacheMaxAgeInSec($requestedCache); 71804fd306cSNickeau return $this->cacheAfterRendering ? $cacheAge : 0; 71904fd306cSNickeau 72004fd306cSNickeau } 72104fd306cSNickeau 72204fd306cSNickeau 72304fd306cSNickeau public function __toString() 72404fd306cSNickeau { 72504fd306cSNickeau 72604fd306cSNickeau return parent::__toString() . " ({$this->getSourceName()}, {$this->getMime()->toString()})"; 72704fd306cSNickeau } 72804fd306cSNickeau 72904fd306cSNickeau 73004fd306cSNickeau /** 73104fd306cSNickeau * @throws ExceptionBadArgument 73204fd306cSNickeau */ 73304fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherMarkup 73404fd306cSNickeau { 73504fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 73604fd306cSNickeau return $this; 73704fd306cSNickeau } 73804fd306cSNickeau 73904fd306cSNickeau 74004fd306cSNickeau /** 74104fd306cSNickeau * @return LocalPath - the cache path is where the result is stored if the cache is on 74204fd306cSNickeau * The cache path may have change due to the cache key rerouting 74304fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 74404fd306cSNickeau * as fetch path 74504fd306cSNickeau */ 74604fd306cSNickeau public function getContentCachePath(): LocalPath 74704fd306cSNickeau { 74804fd306cSNickeau $path = $this->contentCache->cache; 74904fd306cSNickeau return LocalPath::createFromPathString($path); 75004fd306cSNickeau } 75104fd306cSNickeau 75204fd306cSNickeau 75304fd306cSNickeau public function getOutputCacheDependencies(): MarkupCacheDependencies 75404fd306cSNickeau { 75504fd306cSNickeau return $this->outputCacheDependencies; 75604fd306cSNickeau } 75704fd306cSNickeau 75804fd306cSNickeau 75904fd306cSNickeau /** 76004fd306cSNickeau * @return string - with replacement if any 76104fd306cSNickeau * TODO: edit button replacement could be a script tag with a json, permits to do DOM manipulation 76204fd306cSNickeau * @throws ExceptionCompile - if any processing error occurs 76304fd306cSNickeau */ 76404fd306cSNickeau public function getFetchString(): string 76504fd306cSNickeau { 76604fd306cSNickeau $this->processIfNeeded(); 76704fd306cSNickeau 76804fd306cSNickeau if (!$this->isPathExecution()) { 76904fd306cSNickeau return $this->fetchString; 77004fd306cSNickeau } 77104fd306cSNickeau 77204fd306cSNickeau /** 77304fd306cSNickeau * Source path execution 77404fd306cSNickeau * The cache path may have change due to the cache key rerouting 77504fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 77604fd306cSNickeau * as fetch path 77704fd306cSNickeau */ 77804fd306cSNickeau $path = $this->getContentCachePath(); 77904fd306cSNickeau try { 78004fd306cSNickeau $text = FileSystems::getContent($path); 78104fd306cSNickeau } catch (ExceptionNotFound $e) { 78204fd306cSNickeau throw new ExceptionRuntime("Internal error: The fetch path should exists.", self::CANONICAL, 1, $e); 78304fd306cSNickeau } 78404fd306cSNickeau 78504fd306cSNickeau /** 78604fd306cSNickeau * Edit button Processing for XHtml 78704fd306cSNickeau * (Path is mandatory to create the buttons) 78804fd306cSNickeau */ 78904fd306cSNickeau if (!in_array($this->getMime()->getExtension(), ["html", "xhtml"])) { 79004fd306cSNickeau return $text; 79104fd306cSNickeau } 79204fd306cSNickeau try { 79304fd306cSNickeau if ($this->getSourcePath()->toWikiPath()->getDrive() !== WikiPath::MARKUP_DRIVE) { 79404fd306cSNickeau // case when this is a default page in the resource/template directory 79504fd306cSNickeau return EditButton::deleteAll($text); 79604fd306cSNickeau } 79704fd306cSNickeau } catch (ExceptionNotFound|ExceptionCast $e) { 79804fd306cSNickeau // not a wiki path 79904fd306cSNickeau } 80004fd306cSNickeau return EditButton::replaceOrDeleteAll($text); 80104fd306cSNickeau 80204fd306cSNickeau } 80304fd306cSNickeau 80404fd306cSNickeau 80504fd306cSNickeau public function getLabel(): string 80604fd306cSNickeau { 80704fd306cSNickeau try { 80804fd306cSNickeau $sourcePath = $this->getSourcePath(); 80904fd306cSNickeau } catch (ExceptionNotFound $e) { 81004fd306cSNickeau return self::MARKUP_DYNAMIC_EXECUTION_NAME; 81104fd306cSNickeau } 81204fd306cSNickeau return ResourceName::getFromPath($sourcePath); 81304fd306cSNickeau } 81404fd306cSNickeau 81504fd306cSNickeau 81604fd306cSNickeau public function getRequestedContextPath(): WikiPath 81704fd306cSNickeau { 81804fd306cSNickeau return $this->requestedContextPath; 81904fd306cSNickeau } 82004fd306cSNickeau 82104fd306cSNickeau 82204fd306cSNickeau /** 82304fd306cSNickeau * @throws ExceptionNotFound 82404fd306cSNickeau */ 82504fd306cSNickeau public function getSourcePath(): Path 82604fd306cSNickeau { 82704fd306cSNickeau if (isset($this->markupSourcePath)) { 82804fd306cSNickeau return $this->markupSourcePath; 82904fd306cSNickeau } 83004fd306cSNickeau throw new ExceptionNotFound("No source path for this markup"); 83104fd306cSNickeau } 83204fd306cSNickeau 83304fd306cSNickeau /** 83404fd306cSNickeau * Utility class that return the source path 83504fd306cSNickeau * @return Path 83604fd306cSNickeau * @throws ExceptionNotFound 83704fd306cSNickeau */ 83804fd306cSNickeau public function getRequestedExecutingPath(): Path 83904fd306cSNickeau { 84004fd306cSNickeau return $this->getSourcePath(); 84104fd306cSNickeau } 84204fd306cSNickeau 84304fd306cSNickeau /** 84404fd306cSNickeau * @return Path|null - utility class to get the source markup path or null (if this is a markup snippet/string rendering) 84504fd306cSNickeau */ 84604fd306cSNickeau public function getExecutingPathOrNull(): ?Path 84704fd306cSNickeau { 84804fd306cSNickeau try { 84904fd306cSNickeau return $this->getSourcePath(); 85004fd306cSNickeau } catch (ExceptionNotFound $e) { 85104fd306cSNickeau return null; 85204fd306cSNickeau } 85304fd306cSNickeau } 85404fd306cSNickeau 85504fd306cSNickeau 85604fd306cSNickeau /** 85704fd306cSNickeau * @param string $componentId 85804fd306cSNickeau * @return Snippet[] 85904fd306cSNickeau */ 86004fd306cSNickeau public function getSnippetsForComponent(string $componentId): array 86104fd306cSNickeau { 86204fd306cSNickeau 86304fd306cSNickeau $snippets = $this->getSnippets(); 86404fd306cSNickeau $snippetsForComponent = []; 86504fd306cSNickeau foreach ($snippets as $snippet) { 86604fd306cSNickeau try { 86704fd306cSNickeau if ($snippet->getComponentId() === $componentId) { 86804fd306cSNickeau $snippetsForComponent[] = $snippet; 86904fd306cSNickeau } 87004fd306cSNickeau } catch (ExceptionNotFound $e) { 87104fd306cSNickeau // 87204fd306cSNickeau } 87304fd306cSNickeau } 87404fd306cSNickeau return $snippetsForComponent; 87504fd306cSNickeau 87604fd306cSNickeau } 87704fd306cSNickeau 87804fd306cSNickeau /** 87904fd306cSNickeau * @return Snippet[] 88004fd306cSNickeau */ 88104fd306cSNickeau public function getSnippets(): array 88204fd306cSNickeau { 88304fd306cSNickeau 88404fd306cSNickeau $snippets = $this->localSnippets; 88504fd306cSNickeau 88604fd306cSNickeau /** 88704fd306cSNickeau * Old ways where snippets were added to the global scope 88804fd306cSNickeau * and not to the fetcher markup via {@link self::addSnippet()} 88904fd306cSNickeau * 89004fd306cSNickeau * During the transition, we support the two 89104fd306cSNickeau * 89204fd306cSNickeau * Note that with the new system where render code 89304fd306cSNickeau * can access this object via {@link ExecutionContext::getExecutingMarkupHandler()} 89404fd306cSNickeau * the code may had snippets without any id 89504fd306cSNickeau * (For the time being, not yet) 89604fd306cSNickeau */ 89704fd306cSNickeau try { 89804fd306cSNickeau $slotId = $this->getSourcePath()->toWikiPath()->getWikiId(); 89904fd306cSNickeau } catch (ExceptionNotFound $e) { 90004fd306cSNickeau // a markup string run 90104fd306cSNickeau return $snippets; 90204fd306cSNickeau } catch (ExceptionCast $e) { 90304fd306cSNickeau // not a wiki path 90404fd306cSNickeau return $snippets; 90504fd306cSNickeau } 90604fd306cSNickeau 90704fd306cSNickeau $snippetManager = PluginUtility::getSnippetManager(); 90804fd306cSNickeau $oldWaySnippets = $snippetManager->getSnippetsForSlot($slotId); 90904fd306cSNickeau return array_merge($oldWaySnippets, $snippets); 91004fd306cSNickeau 91104fd306cSNickeau } 91204fd306cSNickeau 91304fd306cSNickeau /** 91404fd306cSNickeau * @param Snippet $snippet 91504fd306cSNickeau * @return FetcherMarkup 91604fd306cSNickeau */ 91704fd306cSNickeau public function addSnippet(Snippet $snippet): FetcherMarkup 91804fd306cSNickeau { 91904fd306cSNickeau /** 92004fd306cSNickeau * Snippet should be added only when they can be store 92104fd306cSNickeau * (ie when this is path execution) 92204fd306cSNickeau * If this is not a path execution, the snippet cannot be 92304fd306cSNickeau * stored in a cache and are therefore lost if not used 92404fd306cSNickeau */ 92504fd306cSNickeau 92604fd306cSNickeau /** 92704fd306cSNickeau * If there is a parent markup handler 92804fd306cSNickeau * Store the snippets there 92904fd306cSNickeau */ 93004fd306cSNickeau if (isset($this->parentMarkupHandler)) { 93104fd306cSNickeau $this->parentMarkupHandler->addSnippet($snippet); 93204fd306cSNickeau return $this; 93304fd306cSNickeau } 93404fd306cSNickeau 93504fd306cSNickeau if (!$this->isPathExecution() 93604fd306cSNickeau && !$this->isNonPathStandaloneExecution 93704fd306cSNickeau // In preview, there is no parent handler because we didn't take over 93804fd306cSNickeau && ExecutionContext::getActualOrCreateFromEnv()->getExecutingAction() !== ExecutionContext::PREVIEW_ACTION 93904fd306cSNickeau ) { 94004fd306cSNickeau LogUtility::warning("The execution ($this) is not a path execution. The snippet $snippet will not be preserved after initial rendering. Set the execution as standalone or set a parent markup handler."); 94104fd306cSNickeau } 94204fd306cSNickeau if (!in_array($this->getMime()->toString(), [Mime::XHTML, Mime::HTML])) { 94304fd306cSNickeau LogUtility::warning("The execution ($this) is not a HTML execution. The snippet $snippet will not be preserved because they are reserved for XHMTL execution"); 94404fd306cSNickeau } 94504fd306cSNickeau 94604fd306cSNickeau $snippetGuid = $snippet->getPath()->toUriString(); 94704fd306cSNickeau $this->localSnippets[$snippetGuid] = $snippet; 94804fd306cSNickeau return $this; 94904fd306cSNickeau 95004fd306cSNickeau 95104fd306cSNickeau } 95204fd306cSNickeau 95304fd306cSNickeau /** 95404fd306cSNickeau * @return bool true if the markup string comes from a path 95504fd306cSNickeau * This is motsly important for cache as we use the path as the cache key 95604fd306cSNickeau * (Cache: 95704fd306cSNickeau * * of the {@link self::getInstructions() instructions}, 95804fd306cSNickeau * * of the {@link self::getOutputCacheDependencies() output dependencies} 95904fd306cSNickeau * * of the {@link self::getSnippets() snippets} 96004fd306cSNickeau * * of the {@link self::processMetadataIfNotYetDone() metadata} 96104fd306cSNickeau * 96204fd306cSNickeau * The rule is this is a path execution of the {@link self::$markupSourcePath executing source path} is set. 96304fd306cSNickeau * 96404fd306cSNickeau * Ie this is not a path execution, if the input is: 96504fd306cSNickeau * * {@link self::$requestedInstructions} (used for templating) 96604fd306cSNickeau * * a {@link self::$markupString} (used for test or webcode) 96704fd306cSNickeau * 96804fd306cSNickeau */ 96904fd306cSNickeau public function isPathExecution(): bool 97004fd306cSNickeau { 97104fd306cSNickeau if (isset($this->markupSourcePath)) { 97204fd306cSNickeau return true; 97304fd306cSNickeau } 97404fd306cSNickeau return false; 97504fd306cSNickeau } 97604fd306cSNickeau 97704fd306cSNickeau /** 97804fd306cSNickeau * @throws ExceptionCompile - if any processing errors occurs 97904fd306cSNickeau */ 98004fd306cSNickeau public function processIfNeeded(): FetcherMarkup 98104fd306cSNickeau { 98204fd306cSNickeau 98304fd306cSNickeau if (!$this->shouldProcess()) { 98404fd306cSNickeau return $this; 98504fd306cSNickeau } 98604fd306cSNickeau 98704fd306cSNickeau $this->process(); 98804fd306cSNickeau return $this; 98904fd306cSNickeau 99004fd306cSNickeau } 99104fd306cSNickeau 99204fd306cSNickeau 99304fd306cSNickeau /** 99404fd306cSNickeau * @return array - the markup instructions 99504fd306cSNickeau * @throws ExceptionNotExists - if the executing markup file does not exist 99604fd306cSNickeau */ 99704fd306cSNickeau public function getInstructions(): array 99804fd306cSNickeau { 99904fd306cSNickeau 100004fd306cSNickeau if (isset($this->requestedInstructions)) { 100104fd306cSNickeau 100204fd306cSNickeau return $this->requestedInstructions; 100304fd306cSNickeau 100404fd306cSNickeau } 100504fd306cSNickeau 100604fd306cSNickeau /** 100704fd306cSNickeau * We create a fetcher markup to not have the same {@link self::getId()} 100804fd306cSNickeau * on execution 100904fd306cSNickeau */ 101004fd306cSNickeau if (ExecutionContext::getActualOrCreateFromEnv()->hasExecutingMarkupHandler()) { 101104fd306cSNickeau $fetcherMarkupBuilder = FetcherMarkup::confChild(); 101204fd306cSNickeau } else { 101304fd306cSNickeau $fetcherMarkupBuilder = FetcherMarkup::confRoot(); 101404fd306cSNickeau } 101504fd306cSNickeau $fetcherMarkupBuilder = $fetcherMarkupBuilder 101604fd306cSNickeau ->setRequestedMime(Mime::create(Mime::INSTRUCTIONS)) 101704fd306cSNickeau ->setRequestedRenderer(FetcherMarkupInstructions::NAME) 101804fd306cSNickeau ->setIsDocument($this->isDoc) 101904fd306cSNickeau ->setRequestedContextPath($this->getRequestedContextPath()); 102004fd306cSNickeau if ($this->isPathExecution()) { 102104fd306cSNickeau $fetcherMarkupBuilder->setRequestedExecutingPath($this->getExecutingPathOrFail()); 102204fd306cSNickeau } else { 102304fd306cSNickeau $fetcherMarkupBuilder->setRequestedMarkupString($this->markupString); 102404fd306cSNickeau } 102504fd306cSNickeau $fetcherMarkup = $fetcherMarkupBuilder->build(); 102604fd306cSNickeau return $fetcherMarkup->getProcessedInstructions(); 102704fd306cSNickeau 102804fd306cSNickeau 102904fd306cSNickeau } 103004fd306cSNickeau 103104fd306cSNickeau 103204fd306cSNickeau /** 103304fd306cSNickeau * @return bool - a document 103404fd306cSNickeau * 103504fd306cSNickeau * A document will get an {@link Outline} processing 103604fd306cSNickeau * while a {@link self::isFragment() fragment} will not. 103704fd306cSNickeau */ 103804fd306cSNickeau public function isDocument(): bool 103904fd306cSNickeau { 104004fd306cSNickeau 104104fd306cSNickeau return $this->isDoc; 104204fd306cSNickeau 104304fd306cSNickeau } 104404fd306cSNickeau 104504fd306cSNickeau public function getSnippetManager(): SnippetSystem 104604fd306cSNickeau { 104704fd306cSNickeau return PluginUtility::getSnippetManager(); 104804fd306cSNickeau } 104904fd306cSNickeau 105004fd306cSNickeau /** 105104fd306cSNickeau * @throws ExceptionBadSyntax 105204fd306cSNickeau * @throws ExceptionCompile 105304fd306cSNickeau */ 105404fd306cSNickeau public function getFetchStringAsDom(): XmlDocument 105504fd306cSNickeau { 105604fd306cSNickeau return XmlDocument::createXmlDocFromMarkup($this->getFetchString()); 105704fd306cSNickeau } 105804fd306cSNickeau 105904fd306cSNickeau public function getSnippetsAsHtmlString(): string 106004fd306cSNickeau { 106104fd306cSNickeau 106204fd306cSNickeau try { 106304fd306cSNickeau $globalSnippets = SnippetSystem::getFromContext()->getSnippetsForSlot($this->getRequestedExecutingPath()->toAbsoluteId()); 106404fd306cSNickeau } catch (ExceptionNotFound $e) { 106504fd306cSNickeau // string execution 106604fd306cSNickeau $globalSnippets = []; 106704fd306cSNickeau } 106804fd306cSNickeau $allSnippets = array_merge($globalSnippets, $this->localSnippets); 106904fd306cSNickeau return SnippetSystem::toHtmlFromSnippetArray($allSnippets); 107004fd306cSNickeau 107104fd306cSNickeau } 107204fd306cSNickeau 107304fd306cSNickeau public function isFragment(): bool 107404fd306cSNickeau { 107504fd306cSNickeau return $this->isDocument() === false; 107604fd306cSNickeau } 107704fd306cSNickeau 107804fd306cSNickeau private function getMarkupStringToExecute(): string 107904fd306cSNickeau { 108004fd306cSNickeau if (isset($this->markupString)) { 108104fd306cSNickeau return $this->markupString; 108204fd306cSNickeau } else { 108304fd306cSNickeau try { 108404fd306cSNickeau $sourcePath = $this->getSourcePath(); 108504fd306cSNickeau } catch (ExceptionNotFound $e) { 108604fd306cSNickeau throw new ExceptionRuntimeInternal("A markup or a source markup path should be specified."); 108704fd306cSNickeau } 108804fd306cSNickeau try { 108904fd306cSNickeau return FileSystems::getContent($sourcePath); 109004fd306cSNickeau } catch (ExceptionNotFound $e) { 109104fd306cSNickeau LogUtility::error("The path ($sourcePath) does not exist, we have set the markup to the empty string during rendering. If you want to delete the cache path, ask it via the cache path function", self::CANONICAL, $e); 109204fd306cSNickeau return ""; 109304fd306cSNickeau } 109404fd306cSNickeau } 109504fd306cSNickeau } 109604fd306cSNickeau 109704fd306cSNickeau public function getContextData(): array 109804fd306cSNickeau { 109904fd306cSNickeau if (isset($this->contextData)) { 110004fd306cSNickeau return $this->contextData; 110104fd306cSNickeau } 110204fd306cSNickeau $this->contextData = MarkupPath::createPageFromPathObject($this->getRequestedContextPath())->getMetadataForRendering(); 110304fd306cSNickeau return $this->contextData; 110404fd306cSNickeau } 110504fd306cSNickeau 110604fd306cSNickeau 110704fd306cSNickeau public function getToc(): array 110804fd306cSNickeau { 110904fd306cSNickeau 111004fd306cSNickeau if (isset($this->toc)) { 111104fd306cSNickeau return $this->toc; 111204fd306cSNickeau } 111304fd306cSNickeau try { 111404fd306cSNickeau return TOC::createForPage($this->getRequestedExecutingPath())->getValue(); 111504fd306cSNickeau } catch (ExceptionNotFound $e) { 111604fd306cSNickeau // no executing page or no value 111704fd306cSNickeau } 111804fd306cSNickeau /** 111904fd306cSNickeau * Derived TOC from instructions 112004fd306cSNickeau */ 112104fd306cSNickeau return Outline::createFromCallStack(CallStack::createFromInstructions($this->getInstructions()))->toTocDokuwikiFormat(); 112204fd306cSNickeau 112304fd306cSNickeau 112404fd306cSNickeau } 112504fd306cSNickeau 112604fd306cSNickeau public function getInstructionsPath(): LocalPath 112704fd306cSNickeau { 112804fd306cSNickeau $path = $this->instructionsCache->cache; 112904fd306cSNickeau return LocalPath::createFromPathString($path); 113004fd306cSNickeau } 113104fd306cSNickeau 113204fd306cSNickeau public function getOutline(): Outline 113304fd306cSNickeau { 113404fd306cSNickeau $instructions = $this->getInstructions(); 113504fd306cSNickeau $callStack = CallStack::createFromInstructions($instructions); 113604fd306cSNickeau try { 113704fd306cSNickeau $markupPath = MarkupPath::createPageFromPathObject($this->getRequestedExecutingPath()); 113804fd306cSNickeau } catch (ExceptionNotFound $e) { 113904fd306cSNickeau $markupPath = null; 114004fd306cSNickeau } 114104fd306cSNickeau return Outline::createFromCallStack($callStack, $markupPath); 114204fd306cSNickeau } 114304fd306cSNickeau 114404fd306cSNickeau 114504fd306cSNickeau public function getMetadata(): array 114604fd306cSNickeau { 114704fd306cSNickeau 114804fd306cSNickeau $this->processMetadataIfNotYetDone(); 114904fd306cSNickeau return $this->meta; 115004fd306cSNickeau 115104fd306cSNickeau } 115204fd306cSNickeau 115304fd306cSNickeau 115404fd306cSNickeau /** 115504fd306cSNickeau * Adaptation of {@link p_get_metadata()} 115604fd306cSNickeau * to take into account {@link self::getInstructions()} 115704fd306cSNickeau * where we can just pass our own instructions. 115804fd306cSNickeau * 115904fd306cSNickeau * And yes, adaptation of {@link p_get_metadata()} 116004fd306cSNickeau * that process the metadata. Yeah, it calls {@link p_render_metadata()} 116104fd306cSNickeau * and save them 116204fd306cSNickeau * 116304fd306cSNickeau */ 116404fd306cSNickeau public function processMetadataIfNotYetDone(): FetcherMarkup 116504fd306cSNickeau { 116604fd306cSNickeau 116704fd306cSNickeau /** 116804fd306cSNickeau * Already set ? 116904fd306cSNickeau */ 117004fd306cSNickeau if (isset($this->meta)) { 117104fd306cSNickeau return $this; 117204fd306cSNickeau } 117304fd306cSNickeau 117404fd306cSNickeau $actualMeta = []; 117504fd306cSNickeau 117604fd306cSNickeau /** 117704fd306cSNickeau * We wrap the whole block 117804fd306cSNickeau * because {@link CacheRenderer::useCache()} 117904fd306cSNickeau * and the renderer needs it 118004fd306cSNickeau */ 118104fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv()->setExecutingMarkupHandler($this); 118204fd306cSNickeau try { 118304fd306cSNickeau 118404fd306cSNickeau /** 118504fd306cSNickeau * Can we read from the meta file 118604fd306cSNickeau */ 118704fd306cSNickeau 118804fd306cSNickeau 118904fd306cSNickeau if ($this->isPathExecution()) { 119004fd306cSNickeau 119104fd306cSNickeau /** 119204fd306cSNickeau * If the meta file exists 119304fd306cSNickeau */ 119404fd306cSNickeau if (FileSystems::exists($this->getMetaPathOrFail())) { 119504fd306cSNickeau 119604fd306cSNickeau $executingPath = $this->getExecutingPathOrFail(); 119704fd306cSNickeau $actualMeta = MetadataDokuWikiStore::getOrCreateFromResource(MarkupPath::createPageFromPathObject($executingPath)) 119804fd306cSNickeau ->getDataCurrentAndPersistent(); 119904fd306cSNickeau 120004fd306cSNickeau /** 120104fd306cSNickeau * The metadata useCache function has side effect 120204fd306cSNickeau * and triggers a render that fails if the wiki file does not exists 120304fd306cSNickeau */ 120404fd306cSNickeau $depends['files'][] = $this->instructionsCache->cache; 120504fd306cSNickeau $depends['files'][] = $executingPath->toAbsolutePath()->toAbsoluteId(); 120604fd306cSNickeau $useCache = $this->metaCache->useCache($depends); 120704fd306cSNickeau if ($useCache) { 120804fd306cSNickeau $this->meta = $actualMeta; 120904fd306cSNickeau return $this; 121004fd306cSNickeau } 121104fd306cSNickeau } 121204fd306cSNickeau } 121304fd306cSNickeau 121404fd306cSNickeau /** 121504fd306cSNickeau * Process and derived meta 121604fd306cSNickeau */ 121704fd306cSNickeau try { 121804fd306cSNickeau $wikiId = $this->getRequestedExecutingPath()->toWikiPath()->getWikiId(); 121904fd306cSNickeau } catch (ExceptionCast|ExceptionNotFound $e) { 122004fd306cSNickeau // not a wiki path execution 122104fd306cSNickeau $wikiId = null; 122204fd306cSNickeau } 122304fd306cSNickeau 122404fd306cSNickeau /** 122504fd306cSNickeau * Dokuwiki global variable used to see if the process is in rendering mode 122604fd306cSNickeau * See {@link p_get_metadata()} 122704fd306cSNickeau * Store the original metadata in the global $METADATA_RENDERERS 122804fd306cSNickeau * ({@link p_set_metadata()} use it) 122904fd306cSNickeau */ 123004fd306cSNickeau global $METADATA_RENDERERS; 123104fd306cSNickeau $METADATA_RENDERERS[$wikiId] =& $actualMeta; 123204fd306cSNickeau 123304fd306cSNickeau // add an extra key for the event - to tell event handlers the page whose metadata this is 123404fd306cSNickeau $actualMeta['page'] = $wikiId; 123504fd306cSNickeau $evt = new \dokuwiki\Extension\Event('PARSER_METADATA_RENDER', $actualMeta); 123604fd306cSNickeau if ($evt->advise_before()) { 123704fd306cSNickeau 123804fd306cSNickeau // get instructions (from string or file) 123904fd306cSNickeau $instructions = $this->getInstructions(); 124004fd306cSNickeau 124104fd306cSNickeau // set up the renderer 124204fd306cSNickeau $renderer = new Doku_Renderer_metadata(); 124304fd306cSNickeau 124404fd306cSNickeau 124504fd306cSNickeau /** 124604fd306cSNickeau * Runtime/ Derived metadata 124704fd306cSNickeau * The runtime meta are not even deleted 124804fd306cSNickeau * (See {@link p_render_metadata()} 124904fd306cSNickeau */ 125004fd306cSNickeau $renderer->meta =& $actualMeta['current']; 125104fd306cSNickeau 125204fd306cSNickeau /** 125304fd306cSNickeau * The {@link Doku_Renderer_metadata} 125404fd306cSNickeau * will fail if the file and the date modified property does not exist 125504fd306cSNickeau */ 125604fd306cSNickeau try { 125704fd306cSNickeau $path = $this->getRequestedExecutingPath(); 125804fd306cSNickeau if (!FileSystems::exists($path)) { 125904fd306cSNickeau $renderer->meta['date']['modified'] = null; 126004fd306cSNickeau } 126104fd306cSNickeau } catch (ExceptionNotFound $e) { 126204fd306cSNickeau // ok 126304fd306cSNickeau } 126404fd306cSNickeau 126504fd306cSNickeau /** 126604fd306cSNickeau * The persistent data are now available 126704fd306cSNickeau */ 126804fd306cSNickeau $renderer->persistent =& $actualMeta['persistent']; 126904fd306cSNickeau 127004fd306cSNickeau // Loop through the instructions 127104fd306cSNickeau foreach ($instructions as $instruction) { 127204fd306cSNickeau // execute the callback against the renderer 127304fd306cSNickeau call_user_func_array(array(&$renderer, $instruction[0]), (array)$instruction[1]); 127404fd306cSNickeau } 127504fd306cSNickeau 127604fd306cSNickeau $evt->result = array('current' => &$renderer->meta, 'persistent' => &$renderer->persistent); 127704fd306cSNickeau 127804fd306cSNickeau } 127904fd306cSNickeau $evt->advise_after(); 128004fd306cSNickeau 128104fd306cSNickeau $this->meta = $evt->result; 128204fd306cSNickeau 128304fd306cSNickeau /** 128404fd306cSNickeau * Dokuwiki global variable 128504fd306cSNickeau * See {@link p_get_metadata()} 128604fd306cSNickeau */ 128704fd306cSNickeau unset($METADATA_RENDERERS[$wikiId]); 128804fd306cSNickeau 128904fd306cSNickeau /** 129004fd306cSNickeau * Storage 129104fd306cSNickeau */ 129204fd306cSNickeau if ($wikiId !== null) { 129304fd306cSNickeau p_save_metadata($wikiId, $this->meta); 129404fd306cSNickeau $this->metaCache->storeCache(time()); 129504fd306cSNickeau } 129604fd306cSNickeau 129704fd306cSNickeau } finally { 129804fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 129904fd306cSNickeau } 130004fd306cSNickeau return $this; 130104fd306cSNickeau 130204fd306cSNickeau } 130304fd306cSNickeau 130404fd306cSNickeau /** 130504fd306cSNickeau * @throws ExceptionNotFound 130604fd306cSNickeau */ 130704fd306cSNickeau public function getMetadataPath(): LocalPath 130804fd306cSNickeau { 130904fd306cSNickeau if (isset($this->metaPath)) { 131004fd306cSNickeau return $this->metaPath; 131104fd306cSNickeau } 131204fd306cSNickeau throw new ExceptionNotFound("No meta path for this markup"); 131304fd306cSNickeau } 131404fd306cSNickeau 131504fd306cSNickeau /** 131604fd306cSNickeau * A wrapper from when we are in a code block 131704fd306cSNickeau * were we expect to be a {@link self::isPathExecution()} 131804fd306cSNickeau * All path should then be available 131904fd306cSNickeau * @return Path 132004fd306cSNickeau */ 132104fd306cSNickeau private 132204fd306cSNickeau function getExecutingPathOrFail(): Path 132304fd306cSNickeau { 132404fd306cSNickeau try { 132504fd306cSNickeau return $this->getRequestedExecutingPath(); 132604fd306cSNickeau } catch (ExceptionNotFound $e) { 132704fd306cSNickeau throw new ExceptionRuntime($e); 132804fd306cSNickeau } 132904fd306cSNickeau } 133004fd306cSNickeau 133104fd306cSNickeau /** 133204fd306cSNickeau * A wrapper from when we are in a code block 133304fd306cSNickeau * were we expect to be a {@link self::isPathExecution()} 133404fd306cSNickeau * All path should then be available 133504fd306cSNickeau * @return Path 133604fd306cSNickeau */ 133704fd306cSNickeau private 133804fd306cSNickeau function getMetaPathOrFail() 133904fd306cSNickeau { 134004fd306cSNickeau try { 134104fd306cSNickeau return $this->getMetadataPath(); 134204fd306cSNickeau } catch (ExceptionNotFound $e) { 134304fd306cSNickeau throw new ExceptionRuntime($e); 134404fd306cSNickeau } 134504fd306cSNickeau } 134604fd306cSNickeau 134704fd306cSNickeau /** 134804fd306cSNickeau * Process the instructions 134904fd306cSNickeau * TODO: move to a FetcherMarkup by tree instruction and array/encoding mime 135004fd306cSNickeau * @return $this 135104fd306cSNickeau */ 135204fd306cSNickeau public function processInstructions(): FetcherMarkup 135304fd306cSNickeau { 135404fd306cSNickeau if (isset($this->processedInstructions)) { 135504fd306cSNickeau return $this; 135604fd306cSNickeau } 135704fd306cSNickeau 135804fd306cSNickeau $markup = $this->getMarkupStringToExecute(); 135904fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv() 136004fd306cSNickeau ->setExecutingMarkupHandler($this); 136104fd306cSNickeau try { 136204fd306cSNickeau $markupRenderer = MarkupRenderer::createFromMarkup($markup, $this->getExecutingPathOrNull(), $this->getRequestedContextPath()) 136304fd306cSNickeau ->setRequestedMimeToInstruction(); 136404fd306cSNickeau $instructions = $markupRenderer->getOutput(); 136504fd306cSNickeau if (isset($this->instructionsCache)) { 136604fd306cSNickeau /** 136704fd306cSNickeau * Not a string execution, ie {@link self::isPathExecution()} 136804fd306cSNickeau * a path execution 136904fd306cSNickeau */ 137004fd306cSNickeau $this->instructionsCache->storeCache($instructions); 137104fd306cSNickeau } 137204fd306cSNickeau $this->processedInstructions = $instructions; 137304fd306cSNickeau return $this; 137404fd306cSNickeau } catch (\Exception $e) { 1375*edc35203Sgerardnico throw new ExceptionRuntimeInternal("An error has occurred while processing the instructions. Error: {$e->getMessage()}", self::CANONICAL, 1, $e); 137604fd306cSNickeau } finally { 137704fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 137804fd306cSNickeau } 137904fd306cSNickeau } 138004fd306cSNickeau 138104fd306cSNickeau public function getSnippetCachePath(): LocalPath 138204fd306cSNickeau { 138304fd306cSNickeau $cache = $this->getSnippetCacheStore()->cache; 138404fd306cSNickeau return LocalPath::createFromPathString($cache); 138504fd306cSNickeau 138604fd306cSNickeau } 138704fd306cSNickeau 138804fd306cSNickeau /** 138904fd306cSNickeau * @return string - an execution id to be sure that we don't execute the same twice in recursion 139004fd306cSNickeau */ 139104fd306cSNickeau public function getId(): string 139204fd306cSNickeau { 139304fd306cSNickeau 139404fd306cSNickeau return "({$this->getSourceName()}) to ({$this->builderName} - {$this->getMime()}) with context ({$this->getRequestedContextPath()->toUriString()})"; 139504fd306cSNickeau } 139604fd306cSNickeau 139704fd306cSNickeau /** 139804fd306cSNickeau * @return string - a name for the source (used in {@link self::__toString()} and {@link self::getId() identification}) 139904fd306cSNickeau */ 140004fd306cSNickeau private function getSourceName(): string 140104fd306cSNickeau { 140204fd306cSNickeau if (!$this->isPathExecution()) { 140304fd306cSNickeau if (isset($this->markupString)) { 140404fd306cSNickeau $md5 = md5($this->markupString); 140504fd306cSNickeau return "Markup String Execution ($md5)"; 140604fd306cSNickeau } elseif (isset($this->requestedInstructions)) { 140704fd306cSNickeau return "Markup Instructions Execution"; 140804fd306cSNickeau } else { 140904fd306cSNickeau LogUtility::internalError("The name of the markup handler is unknown"); 141004fd306cSNickeau return "Markup Unknown Execution"; 141104fd306cSNickeau 141204fd306cSNickeau } 141304fd306cSNickeau } else { 141404fd306cSNickeau try { 141504fd306cSNickeau return $this->getSourcePath()->toUriString(); 141604fd306cSNickeau } catch (ExceptionNotFound $e) { 141704fd306cSNickeau throw new ExceptionRuntimeInternal("A source path should be defined if it's not a markup string execution"); 141804fd306cSNickeau } 141904fd306cSNickeau } 142004fd306cSNickeau } 142104fd306cSNickeau 142204fd306cSNickeau 142304fd306cSNickeau /** 142404fd306cSNickeau * TODO: move to a FetcherMarkup by object type output and mime 142504fd306cSNickeau * @return array 142604fd306cSNickeau */ 142704fd306cSNickeau private function getProcessedInstructions(): array 142804fd306cSNickeau { 142904fd306cSNickeau 143004fd306cSNickeau 143104fd306cSNickeau if (!$this->shouldInstructionProcess()) { 143204fd306cSNickeau 143304fd306cSNickeau $this->processedInstructions = $this->instructionsCache->retrieveCache(); 143404fd306cSNickeau 143504fd306cSNickeau } else { 143604fd306cSNickeau 143704fd306cSNickeau $this->processInstructions(); 143804fd306cSNickeau 143904fd306cSNickeau } 144004fd306cSNickeau return $this->processedInstructions; 144104fd306cSNickeau 144204fd306cSNickeau } 144304fd306cSNickeau 144404fd306cSNickeau /** 144504fd306cSNickeau * @throws ExceptionNotFound 144604fd306cSNickeau */ 144704fd306cSNickeau public function getParent(): FetcherMarkup 144804fd306cSNickeau { 144904fd306cSNickeau if (!isset($this->parentMarkupHandler)) { 145004fd306cSNickeau throw new ExceptionNotFound(); 145104fd306cSNickeau } 145204fd306cSNickeau return $this->parentMarkupHandler; 145304fd306cSNickeau } 145404fd306cSNickeau 145504fd306cSNickeau 145604fd306cSNickeau} 1457