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