1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeau 4*04fd306cSNickeaunamespace ComboStrap; 5*04fd306cSNickeau 6*04fd306cSNickeau 7*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore; 8*04fd306cSNickeauuse ComboStrap\Web\Url; 9*04fd306cSNickeauuse ComboStrap\Web\UrlEndpoint; 10*04fd306cSNickeauuse ComboStrap\Xml\XmlDocument; 11*04fd306cSNickeauuse Doku_Renderer_metadata; 12*04fd306cSNickeauuse dokuwiki\Cache\CacheInstructions; 13*04fd306cSNickeauuse dokuwiki\Cache\CacheParser; 14*04fd306cSNickeauuse dokuwiki\Cache\CacheRenderer; 15*04fd306cSNickeauuse Exception; 16*04fd306cSNickeau 17*04fd306cSNickeau 18*04fd306cSNickeau/** 19*04fd306cSNickeau * A class that renders a markup fragment 20*04fd306cSNickeau * This is the context object 21*04fd306cSNickeau * during parsing and rendering is determined by {@link FetcherMarkup} 22*04fd306cSNickeau * 23*04fd306cSNickeau * You can get it in any place via {@link ExecutionContext::getExecutingMarkupHandler()} 24*04fd306cSNickeau * 25*04fd306cSNickeau * It: 26*04fd306cSNickeau * * does not output any full page (HTML document) but only fragment. 27*04fd306cSNickeau * * manage the dependencies (snippets, cache) 28*04fd306cSNickeau * 29*04fd306cSNickeau * This is not really a {@link IFetcher function} because it should not be called 30*04fd306cSNickeau * from the outside but to be able to use the {@link FetcherCache} we need to. 31*04fd306cSNickeau * (as fetcher cache uses the url as unique identifier) 32*04fd306cSNickeau * 33*04fd306cSNickeau * 34*04fd306cSNickeau * TODO: {@link MarkupRenderer} could be one with {@link FetcherMarkup} ? 35*04fd306cSNickeau * 36*04fd306cSNickeau * Not all properties are public to support 37*04fd306cSNickeau * the {@link FetcherMarkupBuilder} pattern. 38*04fd306cSNickeau * Php does not support internal class and protected does not 39*04fd306cSNickeau * work for class on the same namespace. 40*04fd306cSNickeau */ 41*04fd306cSNickeauclass FetcherMarkup extends IFetcherAbs implements IFetcherSource, IFetcherString 42*04fd306cSNickeau{ 43*04fd306cSNickeau 44*04fd306cSNickeau 45*04fd306cSNickeau const XHTML_MODE = "xhtml"; 46*04fd306cSNickeau const MAX_CACHE_AGE = 999999; 47*04fd306cSNickeau 48*04fd306cSNickeau const CANONICAL = "markup-fragment-fetcher"; 49*04fd306cSNickeau 50*04fd306cSNickeau /** 51*04fd306cSNickeau * When the rendering is done from: 52*04fd306cSNickeau * * a string 53*04fd306cSNickeau * * or an instructions (template) 54*04fd306cSNickeau * but not from a file 55*04fd306cSNickeau */ 56*04fd306cSNickeau public const MARKUP_DYNAMIC_EXECUTION_NAME = "markup-dynamic-execution"; 57*04fd306cSNickeau 58*04fd306cSNickeau /** 59*04fd306cSNickeau * @var array - toc in a dokuwiki format 60*04fd306cSNickeau */ 61*04fd306cSNickeau public array $toc; 62*04fd306cSNickeau 63*04fd306cSNickeau /** 64*04fd306cSNickeau * @var CacheParser cache file (may be not set if this is not a {@link self::isPathExecution() execution} 65*04fd306cSNickeau */ 66*04fd306cSNickeau public CacheParser $contentCache; 67*04fd306cSNickeau 68*04fd306cSNickeau /** 69*04fd306cSNickeau * @var string the type of object (known as renderer in Dokuwiki) 70*04fd306cSNickeau */ 71*04fd306cSNickeau public string $builderName; 72*04fd306cSNickeau 73*04fd306cSNickeau public array $requestedInstructions; 74*04fd306cSNickeau public array $contextData; 75*04fd306cSNickeau 76*04fd306cSNickeau public CacheInstructions $instructionsCache; 77*04fd306cSNickeau 78*04fd306cSNickeau /** 79*04fd306cSNickeau * @var CacheRenderer This cache file stores the last render timestamp (see {@link p_get_metadata()} 80*04fd306cSNickeau */ 81*04fd306cSNickeau public CacheRenderer $metaCache; 82*04fd306cSNickeau public LocalPath $metaPath; 83*04fd306cSNickeau 84*04fd306cSNickeau /** 85*04fd306cSNickeau * @var CacheParser 86*04fd306cSNickeau */ 87*04fd306cSNickeau public CacheParser $snippetCache; 88*04fd306cSNickeau 89*04fd306cSNickeau /** 90*04fd306cSNickeau * @var FetcherMarkup - the parent (a instructions run may run inside a path run, ie {@link \syntax_plugin_combo_iterator) 91*04fd306cSNickeau */ 92*04fd306cSNickeau public FetcherMarkup $parentMarkupHandler; 93*04fd306cSNickeau 94*04fd306cSNickeau /** 95*04fd306cSNickeau * @var bool threat the markup as a document (not as a fragment) 96*04fd306cSNickeau */ 97*04fd306cSNickeau public bool $isDoc; 98*04fd306cSNickeau 99*04fd306cSNickeau 100*04fd306cSNickeau public Mime $mime; 101*04fd306cSNickeau private bool $cacheAfterRendering = true; 102*04fd306cSNickeau public MarkupCacheDependencies $outputCacheDependencies; 103*04fd306cSNickeau 104*04fd306cSNickeau 105*04fd306cSNickeau /** 106*04fd306cSNickeau * @var Snippet[] 107*04fd306cSNickeau */ 108*04fd306cSNickeau private array $localSnippets = []; 109*04fd306cSNickeau 110*04fd306cSNickeau public bool $deleteRootBlockElement = false; 111*04fd306cSNickeau 112*04fd306cSNickeau /** 113*04fd306cSNickeau * @var WikiPath the context path, it's important to resolve relative link and to create cache for each context namespace for instance 114*04fd306cSNickeau */ 115*04fd306cSNickeau public WikiPath $requestedContextPath; 116*04fd306cSNickeau 117*04fd306cSNickeau /** 118*04fd306cSNickeau * @var Path the source path of the markup (may be not set if we render a markup string for instance) 119*04fd306cSNickeau */ 120*04fd306cSNickeau public Path $markupSourcePath; 121*04fd306cSNickeau 122*04fd306cSNickeau 123*04fd306cSNickeau public string $markupString; 124*04fd306cSNickeau 125*04fd306cSNickeau /** 126*04fd306cSNickeau * @var bool true if this fetcher has already run 127*04fd306cSNickeau * ( 128*04fd306cSNickeau * Fighting file modified time, even if we cache has been stored, 129*04fd306cSNickeau * the modified time is not always good, this indicator will 130*04fd306cSNickeau * make the processing not run twice) 131*04fd306cSNickeau */ 132*04fd306cSNickeau private bool $hasExecuted = false; 133*04fd306cSNickeau 134*04fd306cSNickeau /** 135*04fd306cSNickeau * The result 136*04fd306cSNickeau * @var string 137*04fd306cSNickeau */ 138*04fd306cSNickeau private string $fetchString; 139*04fd306cSNickeau 140*04fd306cSNickeau 141*04fd306cSNickeau /** 142*04fd306cSNickeau * @var array 143*04fd306cSNickeau */ 144*04fd306cSNickeau private array $meta; 145*04fd306cSNickeau 146*04fd306cSNickeau /** 147*04fd306cSNickeau * @var bool - when a execution is not a {@link self::isPathExecution()}, the snippet will not be stored automatically. 148*04fd306cSNickeau * To avoid this problem, a warning is send if the calling code does not set explicitly that this is specifically a 149*04fd306cSNickeau * standalone execution 150*04fd306cSNickeau */ 151*04fd306cSNickeau public bool $isNonPathStandaloneExecution = false; 152*04fd306cSNickeau /** 153*04fd306cSNickeau * @var array 154*04fd306cSNickeau */ 155*04fd306cSNickeau private array $processedInstructions; 156*04fd306cSNickeau 157*04fd306cSNickeau 158*04fd306cSNickeau /** 159*04fd306cSNickeau * @param Path $executingPath - the path where we can find the markup 160*04fd306cSNickeau * @param ?WikiPath $contextPath - the context path, the requested path in the browser url (from where relative component are resolved (ie links, ...)) 161*04fd306cSNickeau * @return FetcherMarkup 162*04fd306cSNickeau * @throws ExceptionNotExists 163*04fd306cSNickeau */ 164*04fd306cSNickeau public static function createXhtmlMarkupFetcherFromPath(Path $executingPath, WikiPath $contextPath = null): FetcherMarkup 165*04fd306cSNickeau { 166*04fd306cSNickeau if ($contextPath === null) { 167*04fd306cSNickeau try { 168*04fd306cSNickeau $contextPath = $executingPath->toWikiPath(); 169*04fd306cSNickeau } catch (ExceptionCast $e) { 170*04fd306cSNickeau /** 171*04fd306cSNickeau * Not a wiki path, default to the default 172*04fd306cSNickeau */ 173*04fd306cSNickeau $contextPath = ExecutionContext::getActualOrCreateFromEnv()->getDefaultContextPath(); 174*04fd306cSNickeau } 175*04fd306cSNickeau } 176*04fd306cSNickeau return FetcherMarkup::confRoot() 177*04fd306cSNickeau ->setRequestedExecutingPath($executingPath) 178*04fd306cSNickeau ->setRequestedContextPath($contextPath) 179*04fd306cSNickeau ->setRequestedMimeToXhtml() 180*04fd306cSNickeau ->build(); 181*04fd306cSNickeau } 182*04fd306cSNickeau 183*04fd306cSNickeau 184*04fd306cSNickeau public static function confRoot(): FetcherMarkupBuilder 185*04fd306cSNickeau { 186*04fd306cSNickeau return new FetcherMarkupBuilder(); 187*04fd306cSNickeau } 188*04fd306cSNickeau 189*04fd306cSNickeau /** 190*04fd306cSNickeau * Use mostly in test 191*04fd306cSNickeau * The coutnerpart of {@link \ComboStrap\Test\TestUtility::renderText2XhtmlWithoutP()} 192*04fd306cSNickeau * @throws ExceptionNotExists 193*04fd306cSNickeau */ 194*04fd306cSNickeau public static function createStandaloneExecutionFromStringMarkupToXhtml(string $markup): FetcherMarkup 195*04fd306cSNickeau { 196*04fd306cSNickeau return self::confRoot() 197*04fd306cSNickeau ->setRequestedMarkupString($markup) 198*04fd306cSNickeau ->setDeleteRootBlockElement(true) 199*04fd306cSNickeau ->setRequestedContextPathWithDefault() 200*04fd306cSNickeau ->setRequestedMimeToXhtml() 201*04fd306cSNickeau ->setIsStandAloneCodeExecution(true) 202*04fd306cSNickeau ->build(); 203*04fd306cSNickeau } 204*04fd306cSNickeau 205*04fd306cSNickeau /** 206*04fd306cSNickeau * Starts a child fetcher markup 207*04fd306cSNickeau * This is needed for instructions or markup run 208*04fd306cSNickeau * Why ? Because the snippets advertised during this run, need to be stored 209*04fd306cSNickeau * and we need to know the original request path that is in the parent run. 210*04fd306cSNickeau * @return FetcherMarkupBuilder 211*04fd306cSNickeau */ 212*04fd306cSNickeau public static function confChild(): FetcherMarkupBuilder 213*04fd306cSNickeau { 214*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 215*04fd306cSNickeau try { 216*04fd306cSNickeau $executing = $executionContext->getExecutingMarkupHandler(); 217*04fd306cSNickeau } catch (ExceptionNotFound $e) { 218*04fd306cSNickeau if (PluginUtility::isDevOrTest() && $executionContext->getExecutingAction() !== ExecutionContext::PREVIEW_ACTION) { 219*04fd306cSNickeau LogUtility::warning("A markup handler is not running, we couldn't create a child."); 220*04fd306cSNickeau } 221*04fd306cSNickeau return self::confRoot(); 222*04fd306cSNickeau } 223*04fd306cSNickeau return self::confRoot() 224*04fd306cSNickeau ->setParentMarkupHandler($executing) 225*04fd306cSNickeau ->setRequestedContextPath($executing->getRequestedContextPath()); 226*04fd306cSNickeau } 227*04fd306cSNickeau 228*04fd306cSNickeau 229*04fd306cSNickeau /** 230*04fd306cSNickeau * Dokuwiki will wrap the markup in a p element 231*04fd306cSNickeau * if the first element is not a block 232*04fd306cSNickeau * This option permits to delete it. This is used mostly in test to get 233*04fd306cSNickeau * the generated html 234*04fd306cSNickeau */ 235*04fd306cSNickeau public function deleteRootPElementsIfRequested(array &$instructions): void 236*04fd306cSNickeau { 237*04fd306cSNickeau 238*04fd306cSNickeau if (!$this->deleteRootBlockElement) { 239*04fd306cSNickeau return; 240*04fd306cSNickeau } 241*04fd306cSNickeau 242*04fd306cSNickeau /** 243*04fd306cSNickeau * Delete the p added by {@link Block::process()} 244*04fd306cSNickeau * if the plugin of the {@link SyntaxPlugin::getPType() normal} and not in a block 245*04fd306cSNickeau * 246*04fd306cSNickeau * p_open = document_start in renderer 247*04fd306cSNickeau */ 248*04fd306cSNickeau if ($instructions[1][0] !== 'p_open') { 249*04fd306cSNickeau return; 250*04fd306cSNickeau } 251*04fd306cSNickeau unset($instructions[1]); 252*04fd306cSNickeau 253*04fd306cSNickeau /** 254*04fd306cSNickeau * The last p position is not fix 255*04fd306cSNickeau * We may have other calls due for instance 256*04fd306cSNickeau * of {@link \action_plugin_combo_syntaxanalytics} 257*04fd306cSNickeau */ 258*04fd306cSNickeau $n = 1; 259*04fd306cSNickeau while (($lastPBlockPosition = (sizeof($instructions) - $n)) >= 0) { 260*04fd306cSNickeau 261*04fd306cSNickeau /** 262*04fd306cSNickeau * p_open = document_end in renderer 263*04fd306cSNickeau */ 264*04fd306cSNickeau if ($instructions[$lastPBlockPosition][0] == 'p_close') { 265*04fd306cSNickeau unset($instructions[$lastPBlockPosition]); 266*04fd306cSNickeau break; 267*04fd306cSNickeau } else { 268*04fd306cSNickeau $n = $n + 1; 269*04fd306cSNickeau } 270*04fd306cSNickeau } 271*04fd306cSNickeau 272*04fd306cSNickeau } 273*04fd306cSNickeau 274*04fd306cSNickeau /** 275*04fd306cSNickeau * 276*04fd306cSNickeau * @param Url|null $url 277*04fd306cSNickeau * @return Url 278*04fd306cSNickeau * 279*04fd306cSNickeau * Note: The fetch url is the {@link FetcherCache keyCache} 280*04fd306cSNickeau */ 281*04fd306cSNickeau function getFetchUrl(Url $url = null): Url 282*04fd306cSNickeau { 283*04fd306cSNickeau /** 284*04fd306cSNickeau * Overwrite default fetcher endpoint 285*04fd306cSNickeau * that is {@link UrlEndpoint::createFetchUrl()} 286*04fd306cSNickeau */ 287*04fd306cSNickeau $url = UrlEndpoint::createDokuUrl(); 288*04fd306cSNickeau $url = parent::getFetchUrl($url); 289*04fd306cSNickeau try { 290*04fd306cSNickeau $wikiPath = $this->getSourcePath()->toWikiPath(); 291*04fd306cSNickeau $url->addQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $wikiPath->getWikiId()); 292*04fd306cSNickeau $url->addQueryParameter(WikiPath::DRIVE_ATTRIBUTE, $wikiPath->getDrive()); 293*04fd306cSNickeau } catch (ExceptionCast|ExceptionNotFound $e) { 294*04fd306cSNickeau // not an accessible source path 295*04fd306cSNickeau } 296*04fd306cSNickeau $url->addQueryParameter("context-id", $this->getRequestedContextPath()->getWikiId()); 297*04fd306cSNickeau return $url; 298*04fd306cSNickeau 299*04fd306cSNickeau } 300*04fd306cSNickeau 301*04fd306cSNickeau 302*04fd306cSNickeau /** 303*04fd306cSNickeau * @return Mime 304*04fd306cSNickeau */ 305*04fd306cSNickeau public function getMime(): Mime 306*04fd306cSNickeau { 307*04fd306cSNickeau if (isset($this->mime)) { 308*04fd306cSNickeau return $this->mime; 309*04fd306cSNickeau } 310*04fd306cSNickeau 311*04fd306cSNickeau // XHTML default 312*04fd306cSNickeau try { 313*04fd306cSNickeau return Mime::createFromExtension(self::XHTML_MODE); 314*04fd306cSNickeau } catch (ExceptionNotFound $e) { 315*04fd306cSNickeau // should not happen 316*04fd306cSNickeau throw new ExceptionRuntime("Internal error: The XHTML mime was not found.", self::CANONICAL, 1, $e); 317*04fd306cSNickeau } 318*04fd306cSNickeau } 319*04fd306cSNickeau 320*04fd306cSNickeau /** 321*04fd306cSNickeau * TODO: split We should split fetcherMarkup by object type output and {@link Mime} 322*04fd306cSNickeau * @return bool 323*04fd306cSNickeau */ 324*04fd306cSNickeau private function shouldInstructionProcess(): bool 325*04fd306cSNickeau { 326*04fd306cSNickeau 327*04fd306cSNickeau if (!$this->isPathExecution()) { 328*04fd306cSNickeau return true; 329*04fd306cSNickeau } 330*04fd306cSNickeau 331*04fd306cSNickeau if (isset($this->processedInstructions)) { 332*04fd306cSNickeau return false; 333*04fd306cSNickeau } 334*04fd306cSNickeau 335*04fd306cSNickeau /** 336*04fd306cSNickeau * Edge Case 337*04fd306cSNickeau * (as dokuwiki starts the rendering process here 338*04fd306cSNickeau * we need to set the execution id) 339*04fd306cSNickeau */ 340*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv()->setExecutingMarkupHandler($this); 341*04fd306cSNickeau try { 342*04fd306cSNickeau $useCache = $this->instructionsCache->useCache(); 343*04fd306cSNickeau } finally { 344*04fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 345*04fd306cSNickeau } 346*04fd306cSNickeau return ($useCache === false); 347*04fd306cSNickeau } 348*04fd306cSNickeau 349*04fd306cSNickeau public function shouldProcess(): bool 350*04fd306cSNickeau { 351*04fd306cSNickeau 352*04fd306cSNickeau if (!$this->isPathExecution()) { 353*04fd306cSNickeau return true; 354*04fd306cSNickeau } 355*04fd306cSNickeau 356*04fd306cSNickeau if ($this->hasExecuted) { 357*04fd306cSNickeau return false; 358*04fd306cSNickeau } 359*04fd306cSNickeau 360*04fd306cSNickeau /** 361*04fd306cSNickeau * The cache is stored by requested page scope 362*04fd306cSNickeau * 363*04fd306cSNickeau * We set the environment because 364*04fd306cSNickeau * {@link CacheParser::useCache()} may call a parsing of the markup fragment 365*04fd306cSNickeau * And the global environment are not always passed 366*04fd306cSNickeau * in all actions and is needed to log the {@link CacheResult cache 367*04fd306cSNickeau * result} 368*04fd306cSNickeau * 369*04fd306cSNickeau * Use cache should be always called because it trigger 370*04fd306cSNickeau * the event coupled to the cache (ie PARSER_CACHE_USE) 371*04fd306cSNickeau */ 372*04fd306cSNickeau $depends['age'] = $this->getCacheAge(); 373*04fd306cSNickeau if ($this->isFragment()) { 374*04fd306cSNickeau /** 375*04fd306cSNickeau * Fragment may use variables of the requested page 376*04fd306cSNickeau * We have dependency on {@link MarkupCacheDependencies::PAGE_PRIMARY_META_DEPENDENCY} 377*04fd306cSNickeau * but as they may be derived such as the {@link PageTitle} 378*04fd306cSNickeau * comes from the H1 or the feature image comes from the first image in the section 1 379*04fd306cSNickeau * We can't really use this event. 380*04fd306cSNickeau */ 381*04fd306cSNickeau try { 382*04fd306cSNickeau $depends['files'][] = FetcherMarkup::confRoot() 383*04fd306cSNickeau ->setRequestedContextPath($this->getRequestedContextPath()) 384*04fd306cSNickeau ->setRequestedExecutingPath($this->getRequestedContextPath()) 385*04fd306cSNickeau ->setRequestedMimeToMetadata() 386*04fd306cSNickeau ->build() 387*04fd306cSNickeau ->getMetadataPath() 388*04fd306cSNickeau ->toAbsoluteId(); 389*04fd306cSNickeau } catch (ExceptionNotExists|ExceptionNotFound $e) { 390*04fd306cSNickeau /** 391*04fd306cSNickeau * Computer are hard 392*04fd306cSNickeau * At the beginning there is no markup path 393*04fd306cSNickeau * We may get this error then 394*04fd306cSNickeau * 395*04fd306cSNickeau * We don't allow on test 396*04fd306cSNickeau */ 397*04fd306cSNickeau if (PluginUtility::isTest()) { 398*04fd306cSNickeau /** 399*04fd306cSNickeau * The first edit, the page does not exists 400*04fd306cSNickeau */ 401*04fd306cSNickeau $executingAction = ExecutionContext::getActualOrCreateFromEnv()->getExecutingAction(); 402*04fd306cSNickeau if (!in_array($executingAction, [ExecutionContext::EDIT_ACTION, ExecutionContext::PREVIEW_ACTION])) { 403*04fd306cSNickeau LogUtility::error("The metadata path should be known. " . $e->getMessage(), self::CANONICAL, $e); 404*04fd306cSNickeau } 405*04fd306cSNickeau } 406*04fd306cSNickeau } 407*04fd306cSNickeau } 408*04fd306cSNickeau /** 409*04fd306cSNickeau * Edge Case 410*04fd306cSNickeau * (as dokuwiki starts the rendering process here 411*04fd306cSNickeau * we need to set the execution id) 412*04fd306cSNickeau */ 413*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv() 414*04fd306cSNickeau ->setExecutingMarkupHandler($this); 415*04fd306cSNickeau try { 416*04fd306cSNickeau $useCache = $this->contentCache->useCache($depends); 417*04fd306cSNickeau } finally { 418*04fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 419*04fd306cSNickeau } 420*04fd306cSNickeau return ($useCache === false); 421*04fd306cSNickeau 422*04fd306cSNickeau } 423*04fd306cSNickeau 424*04fd306cSNickeau 425*04fd306cSNickeau public 426*04fd306cSNickeau function storeSnippets() 427*04fd306cSNickeau { 428*04fd306cSNickeau 429*04fd306cSNickeau /** 430*04fd306cSNickeau * Snippet 431*04fd306cSNickeau */ 432*04fd306cSNickeau $snippets = $this->getSnippets(); 433*04fd306cSNickeau $jsonDecodeSnippets = SnippetSystem::toJsonArrayFromSlotSnippets($snippets); 434*04fd306cSNickeau 435*04fd306cSNickeau /** 436*04fd306cSNickeau * Cache file 437*04fd306cSNickeau * Using a cache parser, set the page id and will trigger 438*04fd306cSNickeau * the parser cache use event in order to log/report the cache usage 439*04fd306cSNickeau * At {@link action_plugin_combo_cache::createCacheReport()} 440*04fd306cSNickeau */ 441*04fd306cSNickeau $snippetCache = $this->getSnippetCacheStore(); 442*04fd306cSNickeau $this->outputCacheDependencies->rerouteCacheDestination($snippetCache); 443*04fd306cSNickeau 444*04fd306cSNickeau if (count($jsonDecodeSnippets) > 0) { 445*04fd306cSNickeau $data1 = json_encode($jsonDecodeSnippets); 446*04fd306cSNickeau $snippetCache->storeCache($data1); 447*04fd306cSNickeau } else { 448*04fd306cSNickeau $snippetCache->removeCache(); 449*04fd306cSNickeau } 450*04fd306cSNickeau 451*04fd306cSNickeau } 452*04fd306cSNickeau 453*04fd306cSNickeau /** 454*04fd306cSNickeau * This functon loads the snippets in the global array 455*04fd306cSNickeau * by creating them. Not ideal but works for now. 456*04fd306cSNickeau * @return Snippet[] 457*04fd306cSNickeau */ 458*04fd306cSNickeau public 459*04fd306cSNickeau function loadSnippets(): array 460*04fd306cSNickeau { 461*04fd306cSNickeau 462*04fd306cSNickeau $snippetCacheStore = $this->getSnippetCacheStore(); 463*04fd306cSNickeau $data = $snippetCacheStore->retrieveCache(); 464*04fd306cSNickeau $nativeSnippets = []; 465*04fd306cSNickeau if (!empty($data)) { 466*04fd306cSNickeau $jsonDecodeSnippets = json_decode($data, true); 467*04fd306cSNickeau foreach ($jsonDecodeSnippets as $snippet) { 468*04fd306cSNickeau try { 469*04fd306cSNickeau $nativeSnippets[] = Snippet::createFromJson($snippet); 470*04fd306cSNickeau } catch (ExceptionCompile $e) { 471*04fd306cSNickeau LogUtility::error("The snippet json array cannot be build into a snippet object. " . $e->getMessage() . "\n" . ArrayUtility::formatAsString($snippet), LogUtility::SUPPORT_CANONICAL,); 472*04fd306cSNickeau } 473*04fd306cSNickeau } 474*04fd306cSNickeau } 475*04fd306cSNickeau return $nativeSnippets; 476*04fd306cSNickeau 477*04fd306cSNickeau } 478*04fd306cSNickeau 479*04fd306cSNickeau private 480*04fd306cSNickeau function removeSnippets() 481*04fd306cSNickeau { 482*04fd306cSNickeau $snippetCacheFile = $this->getSnippetCacheStore()->cache; 483*04fd306cSNickeau if ($snippetCacheFile !== null) { 484*04fd306cSNickeau if (file_exists($snippetCacheFile)) { 485*04fd306cSNickeau unlink($snippetCacheFile); 486*04fd306cSNickeau } 487*04fd306cSNickeau } 488*04fd306cSNickeau } 489*04fd306cSNickeau 490*04fd306cSNickeau /** 491*04fd306cSNickeau * @return CacheParser - the cache where the snippets are stored 492*04fd306cSNickeau * Cache file 493*04fd306cSNickeau * Using a cache parser, set the page id and will trigger 494*04fd306cSNickeau * the parser cache use event in order to log/report the cache usage 495*04fd306cSNickeau * At {@link action_plugin_combo_cache::createCacheReport()} 496*04fd306cSNickeau */ 497*04fd306cSNickeau public 498*04fd306cSNickeau function getSnippetCacheStore(): CacheParser 499*04fd306cSNickeau { 500*04fd306cSNickeau if (isset($this->snippetCache)) { 501*04fd306cSNickeau return $this->snippetCache; 502*04fd306cSNickeau } 503*04fd306cSNickeau if ($this->isPathExecution()) { 504*04fd306cSNickeau throw new ExceptionRuntimeInternal("A source path should be available as this is a path execution"); 505*04fd306cSNickeau } 506*04fd306cSNickeau throw new ExceptionRuntime("There is no snippet cache store for a non-path execution"); 507*04fd306cSNickeau 508*04fd306cSNickeau } 509*04fd306cSNickeau 510*04fd306cSNickeau 511*04fd306cSNickeau public 512*04fd306cSNickeau function getDependenciesCacheStore(): CacheParser 513*04fd306cSNickeau { 514*04fd306cSNickeau return $this->outputCacheDependencies->getDependenciesCacheStore(); 515*04fd306cSNickeau } 516*04fd306cSNickeau 517*04fd306cSNickeau public 518*04fd306cSNickeau function getDependenciesCachePath(): LocalPath 519*04fd306cSNickeau { 520*04fd306cSNickeau $cachePath = $this->outputCacheDependencies->getDependenciesCacheStore()->cache; 521*04fd306cSNickeau return LocalPath::createFromPathString($cachePath); 522*04fd306cSNickeau } 523*04fd306cSNickeau 524*04fd306cSNickeau /** 525*04fd306cSNickeau * @return LocalPath the fetch path - start the process and returns a path. If the cache is on, return the {@link FetcherMarkup::getContentCachePath()} 526*04fd306cSNickeau * @throws ExceptionCompile 527*04fd306cSNickeau */ 528*04fd306cSNickeau function processIfNeededAndGetFetchPath(): LocalPath 529*04fd306cSNickeau { 530*04fd306cSNickeau $this->processIfNeeded(); 531*04fd306cSNickeau 532*04fd306cSNickeau /** 533*04fd306cSNickeau * The cache path may have change due to the cache key rerouting 534*04fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 535*04fd306cSNickeau * as fetch path 536*04fd306cSNickeau */ 537*04fd306cSNickeau return $this->getContentCachePath(); 538*04fd306cSNickeau 539*04fd306cSNickeau } 540*04fd306cSNickeau 541*04fd306cSNickeau 542*04fd306cSNickeau /** 543*04fd306cSNickeau * @return $this 544*04fd306cSNickeau * @throws ExceptionCompile 545*04fd306cSNickeau */ 546*04fd306cSNickeau public function process(): FetcherMarkup 547*04fd306cSNickeau { 548*04fd306cSNickeau 549*04fd306cSNickeau $this->hasExecuted = true; 550*04fd306cSNickeau 551*04fd306cSNickeau /** 552*04fd306cSNickeau * Rendering 553*04fd306cSNickeau */ 554*04fd306cSNickeau $executionContext = (ExecutionContext::getActualOrCreateFromEnv()); 555*04fd306cSNickeau 556*04fd306cSNickeau $extension = $this->getMime()->getExtension(); 557*04fd306cSNickeau switch ($extension) { 558*04fd306cSNickeau 559*04fd306cSNickeau case MarkupRenderer::METADATA_EXTENSION: 560*04fd306cSNickeau /** 561*04fd306cSNickeau * The user may ask just for the metadata 562*04fd306cSNickeau * and should then use the {@link self::getMetadata()} 563*04fd306cSNickeau * function instead 564*04fd306cSNickeau */ 565*04fd306cSNickeau break; 566*04fd306cSNickeau case MarkupRenderer::INSTRUCTION_EXTENSION: 567*04fd306cSNickeau /** 568*04fd306cSNickeau * The user may ask just for the instuctions 569*04fd306cSNickeau * and should then use the {@link self::getInstructions()} 570*04fd306cSNickeau * function to get the instructions 571*04fd306cSNickeau */ 572*04fd306cSNickeau return $this; 573*04fd306cSNickeau default: 574*04fd306cSNickeau 575*04fd306cSNickeau $instructions = $this->getInstructions(); 576*04fd306cSNickeau 577*04fd306cSNickeau /** 578*04fd306cSNickeau * Edge case: We delete here 579*04fd306cSNickeau * because the instructions may have been created by dokuwiki 580*04fd306cSNickeau * when we test for the cache with {@link CacheParser::useCache()} 581*04fd306cSNickeau */ 582*04fd306cSNickeau if ($this->deleteRootBlockElement) { 583*04fd306cSNickeau self::deleteRootPElementsIfRequested($instructions); 584*04fd306cSNickeau } 585*04fd306cSNickeau 586*04fd306cSNickeau if (!isset($this->builderName)) { 587*04fd306cSNickeau $this->builderName = $this->getMime()->getExtension(); 588*04fd306cSNickeau } 589*04fd306cSNickeau 590*04fd306cSNickeau $executionContext->setExecutingMarkupHandler($this); 591*04fd306cSNickeau try { 592*04fd306cSNickeau if ($this->isDocument()) { 593*04fd306cSNickeau $markupRenderer = MarkupRenderer::createFromMarkupInstructions($instructions, $this) 594*04fd306cSNickeau ->setRequestedMime($this->getMime()) 595*04fd306cSNickeau ->setRendererName($this->builderName); 596*04fd306cSNickeau 597*04fd306cSNickeau $output = $markupRenderer->getOutput(); 598*04fd306cSNickeau if ($output === null && !empty($instructions)) { 599*04fd306cSNickeau LogUtility::error("The renderer ({$this->builderName}) seems to have been not found"); 600*04fd306cSNickeau } 601*04fd306cSNickeau $this->cacheAfterRendering = $markupRenderer->getCacheAfterRendering(); 602*04fd306cSNickeau } else { 603*04fd306cSNickeau $output = MarkupDynamicRender::create($this->builderName)->processInstructions($instructions); 604*04fd306cSNickeau } 605*04fd306cSNickeau } catch (\Exception $e) { 606*04fd306cSNickeau /** 607*04fd306cSNickeau * Example of errors; 608*04fd306cSNickeau * method_exists() expects parameter 2 to be string, array given 609*04fd306cSNickeau * inc\parserutils.php:672 610*04fd306cSNickeau */ 611*04fd306cSNickeau throw new ExceptionCompile("An error has occurred while getting the output. Error: {$e->getMessage()}", self::CANONICAL, 1, $e); 612*04fd306cSNickeau 613*04fd306cSNickeau } finally { 614*04fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 615*04fd306cSNickeau } 616*04fd306cSNickeau if (is_array($output)) { 617*04fd306cSNickeau LogUtility::internalError("The output was an array", self::CANONICAL); 618*04fd306cSNickeau $this->fetchString = serialize($output); 619*04fd306cSNickeau } else { 620*04fd306cSNickeau $this->fetchString = $output; 621*04fd306cSNickeau } 622*04fd306cSNickeau 623*04fd306cSNickeau break; 624*04fd306cSNickeau } 625*04fd306cSNickeau 626*04fd306cSNickeau /** 627*04fd306cSNickeau * Storage of snippets or dependencies 628*04fd306cSNickeau * none if this is not a path execution 629*04fd306cSNickeau * and for now, metadata storage is done by dokuwiki 630*04fd306cSNickeau */ 631*04fd306cSNickeau if (!$this->isPathExecution() || $this->mime->getExtension() === MarkupRenderer::METADATA_EXTENSION) { 632*04fd306cSNickeau return $this; 633*04fd306cSNickeau } 634*04fd306cSNickeau 635*04fd306cSNickeau 636*04fd306cSNickeau /** 637*04fd306cSNickeau * Snippets and cache dependencies are only for HTML rendering 638*04fd306cSNickeau * Otherwise, otherwise other type rendering may override them 639*04fd306cSNickeau * (such as analtyical json, ...) 640*04fd306cSNickeau */ 641*04fd306cSNickeau if (in_array($this->getMime()->toString(), [Mime::XHTML, Mime::HTML])) { 642*04fd306cSNickeau 643*04fd306cSNickeau /** 644*04fd306cSNickeau * We make the Snippet store to Html store an atomic operation 645*04fd306cSNickeau * 646*04fd306cSNickeau * Why ? Because if the rendering of the page is stopped, 647*04fd306cSNickeau * the cache of the HTML page may be stored but not the cache of the snippets 648*04fd306cSNickeau * leading to a bad page because the next rendering will see then no snippets. 649*04fd306cSNickeau */ 650*04fd306cSNickeau try { 651*04fd306cSNickeau $this->storeSnippets(); 652*04fd306cSNickeau } catch (Exception $e) { 653*04fd306cSNickeau // if any write os exception 654*04fd306cSNickeau LogUtility::msg("Error while storing the xhtml content: {$e->getMessage()}"); 655*04fd306cSNickeau $this->removeSnippets(); 656*04fd306cSNickeau } 657*04fd306cSNickeau 658*04fd306cSNickeau /** 659*04fd306cSNickeau * Cache output dependencies 660*04fd306cSNickeau * Reroute the cache output by runtime dependencies 661*04fd306cSNickeau * set during processing 662*04fd306cSNickeau */ 663*04fd306cSNickeau $this->outputCacheDependencies->storeDependencies(); 664*04fd306cSNickeau $this->outputCacheDependencies->rerouteCacheDestination($this->contentCache); 665*04fd306cSNickeau 666*04fd306cSNickeau } 667*04fd306cSNickeau 668*04fd306cSNickeau /** 669*04fd306cSNickeau * We store always the output in the cache 670*04fd306cSNickeau * if the cache is not on, the file is just overwritten 671*04fd306cSNickeau * 672*04fd306cSNickeau * We don't use 673*04fd306cSNickeau * {{@link CacheParser::storeCache()} 674*04fd306cSNickeau * because it uses the protected parameter `__nocache` 675*04fd306cSNickeau * that will disallow the storage 676*04fd306cSNickeau */ 677*04fd306cSNickeau io_saveFile($this->contentCache->cache, $this->fetchString); 678*04fd306cSNickeau 679*04fd306cSNickeau return $this; 680*04fd306cSNickeau } 681*04fd306cSNickeau 682*04fd306cSNickeau 683*04fd306cSNickeau function getBuster(): string 684*04fd306cSNickeau { 685*04fd306cSNickeau // no buster 686*04fd306cSNickeau return ""; 687*04fd306cSNickeau } 688*04fd306cSNickeau 689*04fd306cSNickeau public 690*04fd306cSNickeau function getFetcherName(): string 691*04fd306cSNickeau { 692*04fd306cSNickeau return "markup-fetcher"; 693*04fd306cSNickeau } 694*04fd306cSNickeau 695*04fd306cSNickeau 696*04fd306cSNickeau private function getCacheAge(): int 697*04fd306cSNickeau { 698*04fd306cSNickeau 699*04fd306cSNickeau $extension = $this->getMime()->getExtension(); 700*04fd306cSNickeau switch ($extension) { 701*04fd306cSNickeau case self::XHTML_MODE: 702*04fd306cSNickeau if (!Site::isHtmlRenderCacheOn()) { 703*04fd306cSNickeau return 0; 704*04fd306cSNickeau } 705*04fd306cSNickeau break; 706*04fd306cSNickeau case MarkupRenderer::INSTRUCTION_EXTENSION: 707*04fd306cSNickeau // indefinitely 708*04fd306cSNickeau return self::MAX_CACHE_AGE; 709*04fd306cSNickeau } 710*04fd306cSNickeau try { 711*04fd306cSNickeau $requestedCache = $this->getRequestedCache(); 712*04fd306cSNickeau } catch (ExceptionNotFound $e) { 713*04fd306cSNickeau $requestedCache = IFetcherAbs::RECACHE_VALUE; 714*04fd306cSNickeau } 715*04fd306cSNickeau $cacheAge = $this->getCacheMaxAgeInSec($requestedCache); 716*04fd306cSNickeau return $this->cacheAfterRendering ? $cacheAge : 0; 717*04fd306cSNickeau 718*04fd306cSNickeau } 719*04fd306cSNickeau 720*04fd306cSNickeau 721*04fd306cSNickeau public function __toString() 722*04fd306cSNickeau { 723*04fd306cSNickeau 724*04fd306cSNickeau return parent::__toString() . " ({$this->getSourceName()}, {$this->getMime()->toString()})"; 725*04fd306cSNickeau } 726*04fd306cSNickeau 727*04fd306cSNickeau 728*04fd306cSNickeau /** 729*04fd306cSNickeau * @throws ExceptionBadArgument 730*04fd306cSNickeau */ 731*04fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherMarkup 732*04fd306cSNickeau { 733*04fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 734*04fd306cSNickeau return $this; 735*04fd306cSNickeau } 736*04fd306cSNickeau 737*04fd306cSNickeau 738*04fd306cSNickeau /** 739*04fd306cSNickeau * @return LocalPath - the cache path is where the result is stored if the cache is on 740*04fd306cSNickeau * The cache path may have change due to the cache key rerouting 741*04fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 742*04fd306cSNickeau * as fetch path 743*04fd306cSNickeau */ 744*04fd306cSNickeau public function getContentCachePath(): LocalPath 745*04fd306cSNickeau { 746*04fd306cSNickeau $path = $this->contentCache->cache; 747*04fd306cSNickeau return LocalPath::createFromPathString($path); 748*04fd306cSNickeau } 749*04fd306cSNickeau 750*04fd306cSNickeau 751*04fd306cSNickeau public function getOutputCacheDependencies(): MarkupCacheDependencies 752*04fd306cSNickeau { 753*04fd306cSNickeau return $this->outputCacheDependencies; 754*04fd306cSNickeau } 755*04fd306cSNickeau 756*04fd306cSNickeau 757*04fd306cSNickeau /** 758*04fd306cSNickeau * @return string - with replacement if any 759*04fd306cSNickeau * TODO: edit button replacement could be a script tag with a json, permits to do DOM manipulation 760*04fd306cSNickeau * @throws ExceptionCompile - if any processing error occurs 761*04fd306cSNickeau */ 762*04fd306cSNickeau public function getFetchString(): string 763*04fd306cSNickeau { 764*04fd306cSNickeau $this->processIfNeeded(); 765*04fd306cSNickeau 766*04fd306cSNickeau if (!$this->isPathExecution()) { 767*04fd306cSNickeau return $this->fetchString; 768*04fd306cSNickeau } 769*04fd306cSNickeau 770*04fd306cSNickeau /** 771*04fd306cSNickeau * Source path execution 772*04fd306cSNickeau * The cache path may have change due to the cache key rerouting 773*04fd306cSNickeau * We should there always use the {@link FetcherMarkup::getContentCachePath()} 774*04fd306cSNickeau * as fetch path 775*04fd306cSNickeau */ 776*04fd306cSNickeau $path = $this->getContentCachePath(); 777*04fd306cSNickeau try { 778*04fd306cSNickeau $text = FileSystems::getContent($path); 779*04fd306cSNickeau } catch (ExceptionNotFound $e) { 780*04fd306cSNickeau throw new ExceptionRuntime("Internal error: The fetch path should exists.", self::CANONICAL, 1, $e); 781*04fd306cSNickeau } 782*04fd306cSNickeau 783*04fd306cSNickeau /** 784*04fd306cSNickeau * Edit button Processing for XHtml 785*04fd306cSNickeau * (Path is mandatory to create the buttons) 786*04fd306cSNickeau */ 787*04fd306cSNickeau if (!in_array($this->getMime()->getExtension(), ["html", "xhtml"])) { 788*04fd306cSNickeau return $text; 789*04fd306cSNickeau } 790*04fd306cSNickeau try { 791*04fd306cSNickeau if ($this->getSourcePath()->toWikiPath()->getDrive() !== WikiPath::MARKUP_DRIVE) { 792*04fd306cSNickeau // case when this is a default page in the resource/template directory 793*04fd306cSNickeau return EditButton::deleteAll($text); 794*04fd306cSNickeau } 795*04fd306cSNickeau } catch (ExceptionNotFound|ExceptionCast $e) { 796*04fd306cSNickeau // not a wiki path 797*04fd306cSNickeau } 798*04fd306cSNickeau return EditButton::replaceOrDeleteAll($text); 799*04fd306cSNickeau 800*04fd306cSNickeau } 801*04fd306cSNickeau 802*04fd306cSNickeau 803*04fd306cSNickeau public function getLabel(): string 804*04fd306cSNickeau { 805*04fd306cSNickeau try { 806*04fd306cSNickeau $sourcePath = $this->getSourcePath(); 807*04fd306cSNickeau } catch (ExceptionNotFound $e) { 808*04fd306cSNickeau return self::MARKUP_DYNAMIC_EXECUTION_NAME; 809*04fd306cSNickeau } 810*04fd306cSNickeau return ResourceName::getFromPath($sourcePath); 811*04fd306cSNickeau } 812*04fd306cSNickeau 813*04fd306cSNickeau 814*04fd306cSNickeau public function getRequestedContextPath(): WikiPath 815*04fd306cSNickeau { 816*04fd306cSNickeau return $this->requestedContextPath; 817*04fd306cSNickeau } 818*04fd306cSNickeau 819*04fd306cSNickeau 820*04fd306cSNickeau /** 821*04fd306cSNickeau * @throws ExceptionNotFound 822*04fd306cSNickeau */ 823*04fd306cSNickeau public function getSourcePath(): Path 824*04fd306cSNickeau { 825*04fd306cSNickeau if (isset($this->markupSourcePath)) { 826*04fd306cSNickeau return $this->markupSourcePath; 827*04fd306cSNickeau } 828*04fd306cSNickeau throw new ExceptionNotFound("No source path for this markup"); 829*04fd306cSNickeau } 830*04fd306cSNickeau 831*04fd306cSNickeau /** 832*04fd306cSNickeau * Utility class that return the source path 833*04fd306cSNickeau * @return Path 834*04fd306cSNickeau * @throws ExceptionNotFound 835*04fd306cSNickeau */ 836*04fd306cSNickeau public function getRequestedExecutingPath(): Path 837*04fd306cSNickeau { 838*04fd306cSNickeau return $this->getSourcePath(); 839*04fd306cSNickeau } 840*04fd306cSNickeau 841*04fd306cSNickeau /** 842*04fd306cSNickeau * @return Path|null - utility class to get the source markup path or null (if this is a markup snippet/string rendering) 843*04fd306cSNickeau */ 844*04fd306cSNickeau public function getExecutingPathOrNull(): ?Path 845*04fd306cSNickeau { 846*04fd306cSNickeau try { 847*04fd306cSNickeau return $this->getSourcePath(); 848*04fd306cSNickeau } catch (ExceptionNotFound $e) { 849*04fd306cSNickeau return null; 850*04fd306cSNickeau } 851*04fd306cSNickeau } 852*04fd306cSNickeau 853*04fd306cSNickeau 854*04fd306cSNickeau /** 855*04fd306cSNickeau * @param string $componentId 856*04fd306cSNickeau * @return Snippet[] 857*04fd306cSNickeau */ 858*04fd306cSNickeau public function getSnippetsForComponent(string $componentId): array 859*04fd306cSNickeau { 860*04fd306cSNickeau 861*04fd306cSNickeau $snippets = $this->getSnippets(); 862*04fd306cSNickeau $snippetsForComponent = []; 863*04fd306cSNickeau foreach ($snippets as $snippet) { 864*04fd306cSNickeau try { 865*04fd306cSNickeau if ($snippet->getComponentId() === $componentId) { 866*04fd306cSNickeau $snippetsForComponent[] = $snippet; 867*04fd306cSNickeau } 868*04fd306cSNickeau } catch (ExceptionNotFound $e) { 869*04fd306cSNickeau // 870*04fd306cSNickeau } 871*04fd306cSNickeau } 872*04fd306cSNickeau return $snippetsForComponent; 873*04fd306cSNickeau 874*04fd306cSNickeau } 875*04fd306cSNickeau 876*04fd306cSNickeau /** 877*04fd306cSNickeau * @return Snippet[] 878*04fd306cSNickeau */ 879*04fd306cSNickeau public function getSnippets(): array 880*04fd306cSNickeau { 881*04fd306cSNickeau 882*04fd306cSNickeau $snippets = $this->localSnippets; 883*04fd306cSNickeau 884*04fd306cSNickeau /** 885*04fd306cSNickeau * Old ways where snippets were added to the global scope 886*04fd306cSNickeau * and not to the fetcher markup via {@link self::addSnippet()} 887*04fd306cSNickeau * 888*04fd306cSNickeau * During the transition, we support the two 889*04fd306cSNickeau * 890*04fd306cSNickeau * Note that with the new system where render code 891*04fd306cSNickeau * can access this object via {@link ExecutionContext::getExecutingMarkupHandler()} 892*04fd306cSNickeau * the code may had snippets without any id 893*04fd306cSNickeau * (For the time being, not yet) 894*04fd306cSNickeau */ 895*04fd306cSNickeau try { 896*04fd306cSNickeau $slotId = $this->getSourcePath()->toWikiPath()->getWikiId(); 897*04fd306cSNickeau } catch (ExceptionNotFound $e) { 898*04fd306cSNickeau // a markup string run 899*04fd306cSNickeau return $snippets; 900*04fd306cSNickeau } catch (ExceptionCast $e) { 901*04fd306cSNickeau // not a wiki path 902*04fd306cSNickeau return $snippets; 903*04fd306cSNickeau } 904*04fd306cSNickeau 905*04fd306cSNickeau $snippetManager = PluginUtility::getSnippetManager(); 906*04fd306cSNickeau $oldWaySnippets = $snippetManager->getSnippetsForSlot($slotId); 907*04fd306cSNickeau return array_merge($oldWaySnippets, $snippets); 908*04fd306cSNickeau 909*04fd306cSNickeau } 910*04fd306cSNickeau 911*04fd306cSNickeau /** 912*04fd306cSNickeau * @param Snippet $snippet 913*04fd306cSNickeau * @return FetcherMarkup 914*04fd306cSNickeau */ 915*04fd306cSNickeau public function addSnippet(Snippet $snippet): FetcherMarkup 916*04fd306cSNickeau { 917*04fd306cSNickeau /** 918*04fd306cSNickeau * Snippet should be added only when they can be store 919*04fd306cSNickeau * (ie when this is path execution) 920*04fd306cSNickeau * If this is not a path execution, the snippet cannot be 921*04fd306cSNickeau * stored in a cache and are therefore lost if not used 922*04fd306cSNickeau */ 923*04fd306cSNickeau 924*04fd306cSNickeau /** 925*04fd306cSNickeau * If there is a parent markup handler 926*04fd306cSNickeau * Store the snippets there 927*04fd306cSNickeau */ 928*04fd306cSNickeau if (isset($this->parentMarkupHandler)) { 929*04fd306cSNickeau $this->parentMarkupHandler->addSnippet($snippet); 930*04fd306cSNickeau return $this; 931*04fd306cSNickeau } 932*04fd306cSNickeau 933*04fd306cSNickeau if (!$this->isPathExecution() 934*04fd306cSNickeau && !$this->isNonPathStandaloneExecution 935*04fd306cSNickeau // In preview, there is no parent handler because we didn't take over 936*04fd306cSNickeau && ExecutionContext::getActualOrCreateFromEnv()->getExecutingAction() !== ExecutionContext::PREVIEW_ACTION 937*04fd306cSNickeau ) { 938*04fd306cSNickeau 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."); 939*04fd306cSNickeau } 940*04fd306cSNickeau if (!in_array($this->getMime()->toString(), [Mime::XHTML, Mime::HTML])) { 941*04fd306cSNickeau LogUtility::warning("The execution ($this) is not a HTML execution. The snippet $snippet will not be preserved because they are reserved for XHMTL execution"); 942*04fd306cSNickeau } 943*04fd306cSNickeau 944*04fd306cSNickeau $snippetGuid = $snippet->getPath()->toUriString(); 945*04fd306cSNickeau $this->localSnippets[$snippetGuid] = $snippet; 946*04fd306cSNickeau return $this; 947*04fd306cSNickeau 948*04fd306cSNickeau 949*04fd306cSNickeau } 950*04fd306cSNickeau 951*04fd306cSNickeau /** 952*04fd306cSNickeau * @return bool true if the markup string comes from a path 953*04fd306cSNickeau * This is motsly important for cache as we use the path as the cache key 954*04fd306cSNickeau * (Cache: 955*04fd306cSNickeau * * of the {@link self::getInstructions() instructions}, 956*04fd306cSNickeau * * of the {@link self::getOutputCacheDependencies() output dependencies} 957*04fd306cSNickeau * * of the {@link self::getSnippets() snippets} 958*04fd306cSNickeau * * of the {@link self::processMetadataIfNotYetDone() metadata} 959*04fd306cSNickeau * 960*04fd306cSNickeau * The rule is this is a path execution of the {@link self::$markupSourcePath executing source path} is set. 961*04fd306cSNickeau * 962*04fd306cSNickeau * Ie this is not a path execution, if the input is: 963*04fd306cSNickeau * * {@link self::$requestedInstructions} (used for templating) 964*04fd306cSNickeau * * a {@link self::$markupString} (used for test or webcode) 965*04fd306cSNickeau * 966*04fd306cSNickeau */ 967*04fd306cSNickeau public function isPathExecution(): bool 968*04fd306cSNickeau { 969*04fd306cSNickeau if (isset($this->markupSourcePath)) { 970*04fd306cSNickeau return true; 971*04fd306cSNickeau } 972*04fd306cSNickeau return false; 973*04fd306cSNickeau } 974*04fd306cSNickeau 975*04fd306cSNickeau /** 976*04fd306cSNickeau * @throws ExceptionCompile - if any processing errors occurs 977*04fd306cSNickeau */ 978*04fd306cSNickeau public function processIfNeeded(): FetcherMarkup 979*04fd306cSNickeau { 980*04fd306cSNickeau 981*04fd306cSNickeau if (!$this->shouldProcess()) { 982*04fd306cSNickeau return $this; 983*04fd306cSNickeau } 984*04fd306cSNickeau 985*04fd306cSNickeau $this->process(); 986*04fd306cSNickeau return $this; 987*04fd306cSNickeau 988*04fd306cSNickeau } 989*04fd306cSNickeau 990*04fd306cSNickeau 991*04fd306cSNickeau /** 992*04fd306cSNickeau * @return array - the markup instructions 993*04fd306cSNickeau * @throws ExceptionNotExists - if the executing markup file does not exist 994*04fd306cSNickeau */ 995*04fd306cSNickeau public function getInstructions(): array 996*04fd306cSNickeau { 997*04fd306cSNickeau 998*04fd306cSNickeau if (isset($this->requestedInstructions)) { 999*04fd306cSNickeau 1000*04fd306cSNickeau return $this->requestedInstructions; 1001*04fd306cSNickeau 1002*04fd306cSNickeau } 1003*04fd306cSNickeau 1004*04fd306cSNickeau /** 1005*04fd306cSNickeau * We create a fetcher markup to not have the same {@link self::getId()} 1006*04fd306cSNickeau * on execution 1007*04fd306cSNickeau */ 1008*04fd306cSNickeau if (ExecutionContext::getActualOrCreateFromEnv()->hasExecutingMarkupHandler()) { 1009*04fd306cSNickeau $fetcherMarkupBuilder = FetcherMarkup::confChild(); 1010*04fd306cSNickeau } else { 1011*04fd306cSNickeau $fetcherMarkupBuilder = FetcherMarkup::confRoot(); 1012*04fd306cSNickeau } 1013*04fd306cSNickeau $fetcherMarkupBuilder = $fetcherMarkupBuilder 1014*04fd306cSNickeau ->setRequestedMime(Mime::create(Mime::INSTRUCTIONS)) 1015*04fd306cSNickeau ->setRequestedRenderer(FetcherMarkupInstructions::NAME) 1016*04fd306cSNickeau ->setIsDocument($this->isDoc) 1017*04fd306cSNickeau ->setRequestedContextPath($this->getRequestedContextPath()); 1018*04fd306cSNickeau if ($this->isPathExecution()) { 1019*04fd306cSNickeau $fetcherMarkupBuilder->setRequestedExecutingPath($this->getExecutingPathOrFail()); 1020*04fd306cSNickeau } else { 1021*04fd306cSNickeau $fetcherMarkupBuilder->setRequestedMarkupString($this->markupString); 1022*04fd306cSNickeau } 1023*04fd306cSNickeau $fetcherMarkup = $fetcherMarkupBuilder->build(); 1024*04fd306cSNickeau return $fetcherMarkup->getProcessedInstructions(); 1025*04fd306cSNickeau 1026*04fd306cSNickeau 1027*04fd306cSNickeau } 1028*04fd306cSNickeau 1029*04fd306cSNickeau 1030*04fd306cSNickeau /** 1031*04fd306cSNickeau * @return bool - a document 1032*04fd306cSNickeau * 1033*04fd306cSNickeau * A document will get an {@link Outline} processing 1034*04fd306cSNickeau * while a {@link self::isFragment() fragment} will not. 1035*04fd306cSNickeau */ 1036*04fd306cSNickeau public function isDocument(): bool 1037*04fd306cSNickeau { 1038*04fd306cSNickeau 1039*04fd306cSNickeau return $this->isDoc; 1040*04fd306cSNickeau 1041*04fd306cSNickeau } 1042*04fd306cSNickeau 1043*04fd306cSNickeau public function getSnippetManager(): SnippetSystem 1044*04fd306cSNickeau { 1045*04fd306cSNickeau return PluginUtility::getSnippetManager(); 1046*04fd306cSNickeau } 1047*04fd306cSNickeau 1048*04fd306cSNickeau /** 1049*04fd306cSNickeau * @throws ExceptionBadSyntax 1050*04fd306cSNickeau * @throws ExceptionCompile 1051*04fd306cSNickeau */ 1052*04fd306cSNickeau public function getFetchStringAsDom(): XmlDocument 1053*04fd306cSNickeau { 1054*04fd306cSNickeau return XmlDocument::createXmlDocFromMarkup($this->getFetchString()); 1055*04fd306cSNickeau } 1056*04fd306cSNickeau 1057*04fd306cSNickeau public function getSnippetsAsHtmlString(): string 1058*04fd306cSNickeau { 1059*04fd306cSNickeau 1060*04fd306cSNickeau try { 1061*04fd306cSNickeau $globalSnippets = SnippetSystem::getFromContext()->getSnippetsForSlot($this->getRequestedExecutingPath()->toAbsoluteId()); 1062*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1063*04fd306cSNickeau // string execution 1064*04fd306cSNickeau $globalSnippets = []; 1065*04fd306cSNickeau } 1066*04fd306cSNickeau $allSnippets = array_merge($globalSnippets, $this->localSnippets); 1067*04fd306cSNickeau return SnippetSystem::toHtmlFromSnippetArray($allSnippets); 1068*04fd306cSNickeau 1069*04fd306cSNickeau } 1070*04fd306cSNickeau 1071*04fd306cSNickeau public function isFragment(): bool 1072*04fd306cSNickeau { 1073*04fd306cSNickeau return $this->isDocument() === false; 1074*04fd306cSNickeau } 1075*04fd306cSNickeau 1076*04fd306cSNickeau private function getMarkupStringToExecute(): string 1077*04fd306cSNickeau { 1078*04fd306cSNickeau if (isset($this->markupString)) { 1079*04fd306cSNickeau return $this->markupString; 1080*04fd306cSNickeau } else { 1081*04fd306cSNickeau try { 1082*04fd306cSNickeau $sourcePath = $this->getSourcePath(); 1083*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1084*04fd306cSNickeau throw new ExceptionRuntimeInternal("A markup or a source markup path should be specified."); 1085*04fd306cSNickeau } 1086*04fd306cSNickeau try { 1087*04fd306cSNickeau return FileSystems::getContent($sourcePath); 1088*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1089*04fd306cSNickeau 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); 1090*04fd306cSNickeau return ""; 1091*04fd306cSNickeau } 1092*04fd306cSNickeau } 1093*04fd306cSNickeau } 1094*04fd306cSNickeau 1095*04fd306cSNickeau public function getContextData(): array 1096*04fd306cSNickeau { 1097*04fd306cSNickeau if (isset($this->contextData)) { 1098*04fd306cSNickeau return $this->contextData; 1099*04fd306cSNickeau } 1100*04fd306cSNickeau $this->contextData = MarkupPath::createPageFromPathObject($this->getRequestedContextPath())->getMetadataForRendering(); 1101*04fd306cSNickeau return $this->contextData; 1102*04fd306cSNickeau } 1103*04fd306cSNickeau 1104*04fd306cSNickeau 1105*04fd306cSNickeau public function getToc(): array 1106*04fd306cSNickeau { 1107*04fd306cSNickeau 1108*04fd306cSNickeau if (isset($this->toc)) { 1109*04fd306cSNickeau return $this->toc; 1110*04fd306cSNickeau } 1111*04fd306cSNickeau try { 1112*04fd306cSNickeau return TOC::createForPage($this->getRequestedExecutingPath())->getValue(); 1113*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1114*04fd306cSNickeau // no executing page or no value 1115*04fd306cSNickeau } 1116*04fd306cSNickeau /** 1117*04fd306cSNickeau * Derived TOC from instructions 1118*04fd306cSNickeau */ 1119*04fd306cSNickeau return Outline::createFromCallStack(CallStack::createFromInstructions($this->getInstructions()))->toTocDokuwikiFormat(); 1120*04fd306cSNickeau 1121*04fd306cSNickeau 1122*04fd306cSNickeau } 1123*04fd306cSNickeau 1124*04fd306cSNickeau public function getInstructionsPath(): LocalPath 1125*04fd306cSNickeau { 1126*04fd306cSNickeau $path = $this->instructionsCache->cache; 1127*04fd306cSNickeau return LocalPath::createFromPathString($path); 1128*04fd306cSNickeau } 1129*04fd306cSNickeau 1130*04fd306cSNickeau public function getOutline(): Outline 1131*04fd306cSNickeau { 1132*04fd306cSNickeau $instructions = $this->getInstructions(); 1133*04fd306cSNickeau $callStack = CallStack::createFromInstructions($instructions); 1134*04fd306cSNickeau try { 1135*04fd306cSNickeau $markupPath = MarkupPath::createPageFromPathObject($this->getRequestedExecutingPath()); 1136*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1137*04fd306cSNickeau $markupPath = null; 1138*04fd306cSNickeau } 1139*04fd306cSNickeau return Outline::createFromCallStack($callStack, $markupPath); 1140*04fd306cSNickeau } 1141*04fd306cSNickeau 1142*04fd306cSNickeau 1143*04fd306cSNickeau public function getMetadata(): array 1144*04fd306cSNickeau { 1145*04fd306cSNickeau 1146*04fd306cSNickeau $this->processMetadataIfNotYetDone(); 1147*04fd306cSNickeau return $this->meta; 1148*04fd306cSNickeau 1149*04fd306cSNickeau } 1150*04fd306cSNickeau 1151*04fd306cSNickeau 1152*04fd306cSNickeau /** 1153*04fd306cSNickeau * Adaptation of {@link p_get_metadata()} 1154*04fd306cSNickeau * to take into account {@link self::getInstructions()} 1155*04fd306cSNickeau * where we can just pass our own instructions. 1156*04fd306cSNickeau * 1157*04fd306cSNickeau * And yes, adaptation of {@link p_get_metadata()} 1158*04fd306cSNickeau * that process the metadata. Yeah, it calls {@link p_render_metadata()} 1159*04fd306cSNickeau * and save them 1160*04fd306cSNickeau * 1161*04fd306cSNickeau */ 1162*04fd306cSNickeau public function processMetadataIfNotYetDone(): FetcherMarkup 1163*04fd306cSNickeau { 1164*04fd306cSNickeau 1165*04fd306cSNickeau /** 1166*04fd306cSNickeau * Already set ? 1167*04fd306cSNickeau */ 1168*04fd306cSNickeau if (isset($this->meta)) { 1169*04fd306cSNickeau return $this; 1170*04fd306cSNickeau } 1171*04fd306cSNickeau 1172*04fd306cSNickeau $actualMeta = []; 1173*04fd306cSNickeau 1174*04fd306cSNickeau /** 1175*04fd306cSNickeau * We wrap the whole block 1176*04fd306cSNickeau * because {@link CacheRenderer::useCache()} 1177*04fd306cSNickeau * and the renderer needs it 1178*04fd306cSNickeau */ 1179*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv()->setExecutingMarkupHandler($this); 1180*04fd306cSNickeau try { 1181*04fd306cSNickeau 1182*04fd306cSNickeau /** 1183*04fd306cSNickeau * Can we read from the meta file 1184*04fd306cSNickeau */ 1185*04fd306cSNickeau 1186*04fd306cSNickeau 1187*04fd306cSNickeau if ($this->isPathExecution()) { 1188*04fd306cSNickeau 1189*04fd306cSNickeau /** 1190*04fd306cSNickeau * If the meta file exists 1191*04fd306cSNickeau */ 1192*04fd306cSNickeau if (FileSystems::exists($this->getMetaPathOrFail())) { 1193*04fd306cSNickeau 1194*04fd306cSNickeau $executingPath = $this->getExecutingPathOrFail(); 1195*04fd306cSNickeau $actualMeta = MetadataDokuWikiStore::getOrCreateFromResource(MarkupPath::createPageFromPathObject($executingPath)) 1196*04fd306cSNickeau ->getDataCurrentAndPersistent(); 1197*04fd306cSNickeau 1198*04fd306cSNickeau /** 1199*04fd306cSNickeau * The metadata useCache function has side effect 1200*04fd306cSNickeau * and triggers a render that fails if the wiki file does not exists 1201*04fd306cSNickeau */ 1202*04fd306cSNickeau $depends['files'][] = $this->instructionsCache->cache; 1203*04fd306cSNickeau $depends['files'][] = $executingPath->toAbsolutePath()->toAbsoluteId(); 1204*04fd306cSNickeau $useCache = $this->metaCache->useCache($depends); 1205*04fd306cSNickeau if ($useCache) { 1206*04fd306cSNickeau $this->meta = $actualMeta; 1207*04fd306cSNickeau return $this; 1208*04fd306cSNickeau } 1209*04fd306cSNickeau } 1210*04fd306cSNickeau } 1211*04fd306cSNickeau 1212*04fd306cSNickeau /** 1213*04fd306cSNickeau * Process and derived meta 1214*04fd306cSNickeau */ 1215*04fd306cSNickeau try { 1216*04fd306cSNickeau $wikiId = $this->getRequestedExecutingPath()->toWikiPath()->getWikiId(); 1217*04fd306cSNickeau } catch (ExceptionCast|ExceptionNotFound $e) { 1218*04fd306cSNickeau // not a wiki path execution 1219*04fd306cSNickeau $wikiId = null; 1220*04fd306cSNickeau } 1221*04fd306cSNickeau 1222*04fd306cSNickeau /** 1223*04fd306cSNickeau * Dokuwiki global variable used to see if the process is in rendering mode 1224*04fd306cSNickeau * See {@link p_get_metadata()} 1225*04fd306cSNickeau * Store the original metadata in the global $METADATA_RENDERERS 1226*04fd306cSNickeau * ({@link p_set_metadata()} use it) 1227*04fd306cSNickeau */ 1228*04fd306cSNickeau global $METADATA_RENDERERS; 1229*04fd306cSNickeau $METADATA_RENDERERS[$wikiId] =& $actualMeta; 1230*04fd306cSNickeau 1231*04fd306cSNickeau // add an extra key for the event - to tell event handlers the page whose metadata this is 1232*04fd306cSNickeau $actualMeta['page'] = $wikiId; 1233*04fd306cSNickeau $evt = new \dokuwiki\Extension\Event('PARSER_METADATA_RENDER', $actualMeta); 1234*04fd306cSNickeau if ($evt->advise_before()) { 1235*04fd306cSNickeau 1236*04fd306cSNickeau // get instructions (from string or file) 1237*04fd306cSNickeau $instructions = $this->getInstructions(); 1238*04fd306cSNickeau 1239*04fd306cSNickeau // set up the renderer 1240*04fd306cSNickeau $renderer = new Doku_Renderer_metadata(); 1241*04fd306cSNickeau 1242*04fd306cSNickeau 1243*04fd306cSNickeau /** 1244*04fd306cSNickeau * Runtime/ Derived metadata 1245*04fd306cSNickeau * The runtime meta are not even deleted 1246*04fd306cSNickeau * (See {@link p_render_metadata()} 1247*04fd306cSNickeau */ 1248*04fd306cSNickeau $renderer->meta =& $actualMeta['current']; 1249*04fd306cSNickeau 1250*04fd306cSNickeau /** 1251*04fd306cSNickeau * The {@link Doku_Renderer_metadata} 1252*04fd306cSNickeau * will fail if the file and the date modified property does not exist 1253*04fd306cSNickeau */ 1254*04fd306cSNickeau try { 1255*04fd306cSNickeau $path = $this->getRequestedExecutingPath(); 1256*04fd306cSNickeau if (!FileSystems::exists($path)) { 1257*04fd306cSNickeau $renderer->meta['date']['modified'] = null; 1258*04fd306cSNickeau } 1259*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1260*04fd306cSNickeau // ok 1261*04fd306cSNickeau } 1262*04fd306cSNickeau 1263*04fd306cSNickeau /** 1264*04fd306cSNickeau * The persistent data are now available 1265*04fd306cSNickeau */ 1266*04fd306cSNickeau $renderer->persistent =& $actualMeta['persistent']; 1267*04fd306cSNickeau 1268*04fd306cSNickeau // Loop through the instructions 1269*04fd306cSNickeau foreach ($instructions as $instruction) { 1270*04fd306cSNickeau // execute the callback against the renderer 1271*04fd306cSNickeau call_user_func_array(array(&$renderer, $instruction[0]), (array)$instruction[1]); 1272*04fd306cSNickeau } 1273*04fd306cSNickeau 1274*04fd306cSNickeau $evt->result = array('current' => &$renderer->meta, 'persistent' => &$renderer->persistent); 1275*04fd306cSNickeau 1276*04fd306cSNickeau } 1277*04fd306cSNickeau $evt->advise_after(); 1278*04fd306cSNickeau 1279*04fd306cSNickeau $this->meta = $evt->result; 1280*04fd306cSNickeau 1281*04fd306cSNickeau /** 1282*04fd306cSNickeau * Dokuwiki global variable 1283*04fd306cSNickeau * See {@link p_get_metadata()} 1284*04fd306cSNickeau */ 1285*04fd306cSNickeau unset($METADATA_RENDERERS[$wikiId]); 1286*04fd306cSNickeau 1287*04fd306cSNickeau /** 1288*04fd306cSNickeau * Storage 1289*04fd306cSNickeau */ 1290*04fd306cSNickeau if ($wikiId !== null) { 1291*04fd306cSNickeau p_save_metadata($wikiId, $this->meta); 1292*04fd306cSNickeau $this->metaCache->storeCache(time()); 1293*04fd306cSNickeau } 1294*04fd306cSNickeau 1295*04fd306cSNickeau } finally { 1296*04fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 1297*04fd306cSNickeau } 1298*04fd306cSNickeau return $this; 1299*04fd306cSNickeau 1300*04fd306cSNickeau } 1301*04fd306cSNickeau 1302*04fd306cSNickeau /** 1303*04fd306cSNickeau * @throws ExceptionNotFound 1304*04fd306cSNickeau */ 1305*04fd306cSNickeau public function getMetadataPath(): LocalPath 1306*04fd306cSNickeau { 1307*04fd306cSNickeau if (isset($this->metaPath)) { 1308*04fd306cSNickeau return $this->metaPath; 1309*04fd306cSNickeau } 1310*04fd306cSNickeau throw new ExceptionNotFound("No meta path for this markup"); 1311*04fd306cSNickeau } 1312*04fd306cSNickeau 1313*04fd306cSNickeau /** 1314*04fd306cSNickeau * A wrapper from when we are in a code block 1315*04fd306cSNickeau * were we expect to be a {@link self::isPathExecution()} 1316*04fd306cSNickeau * All path should then be available 1317*04fd306cSNickeau * @return Path 1318*04fd306cSNickeau */ 1319*04fd306cSNickeau private 1320*04fd306cSNickeau function getExecutingPathOrFail(): Path 1321*04fd306cSNickeau { 1322*04fd306cSNickeau try { 1323*04fd306cSNickeau return $this->getRequestedExecutingPath(); 1324*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1325*04fd306cSNickeau throw new ExceptionRuntime($e); 1326*04fd306cSNickeau } 1327*04fd306cSNickeau } 1328*04fd306cSNickeau 1329*04fd306cSNickeau /** 1330*04fd306cSNickeau * A wrapper from when we are in a code block 1331*04fd306cSNickeau * were we expect to be a {@link self::isPathExecution()} 1332*04fd306cSNickeau * All path should then be available 1333*04fd306cSNickeau * @return Path 1334*04fd306cSNickeau */ 1335*04fd306cSNickeau private 1336*04fd306cSNickeau function getMetaPathOrFail() 1337*04fd306cSNickeau { 1338*04fd306cSNickeau try { 1339*04fd306cSNickeau return $this->getMetadataPath(); 1340*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1341*04fd306cSNickeau throw new ExceptionRuntime($e); 1342*04fd306cSNickeau } 1343*04fd306cSNickeau } 1344*04fd306cSNickeau 1345*04fd306cSNickeau /** 1346*04fd306cSNickeau * Process the instructions 1347*04fd306cSNickeau * TODO: move to a FetcherMarkup by tree instruction and array/encoding mime 1348*04fd306cSNickeau * @return $this 1349*04fd306cSNickeau */ 1350*04fd306cSNickeau public function processInstructions(): FetcherMarkup 1351*04fd306cSNickeau { 1352*04fd306cSNickeau if (isset($this->processedInstructions)) { 1353*04fd306cSNickeau return $this; 1354*04fd306cSNickeau } 1355*04fd306cSNickeau 1356*04fd306cSNickeau $markup = $this->getMarkupStringToExecute(); 1357*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv() 1358*04fd306cSNickeau ->setExecutingMarkupHandler($this); 1359*04fd306cSNickeau try { 1360*04fd306cSNickeau $markupRenderer = MarkupRenderer::createFromMarkup($markup, $this->getExecutingPathOrNull(), $this->getRequestedContextPath()) 1361*04fd306cSNickeau ->setRequestedMimeToInstruction(); 1362*04fd306cSNickeau $instructions = $markupRenderer->getOutput(); 1363*04fd306cSNickeau if (isset($this->instructionsCache)) { 1364*04fd306cSNickeau /** 1365*04fd306cSNickeau * Not a string execution, ie {@link self::isPathExecution()} 1366*04fd306cSNickeau * a path execution 1367*04fd306cSNickeau */ 1368*04fd306cSNickeau $this->instructionsCache->storeCache($instructions); 1369*04fd306cSNickeau } 1370*04fd306cSNickeau $this->processedInstructions = $instructions; 1371*04fd306cSNickeau return $this; 1372*04fd306cSNickeau } catch (\Exception $e) { 1373*04fd306cSNickeau throw new ExceptionRuntimeInternal("An error has occurred while getting the output. Error: {$e->getMessage()}", self::CANONICAL, 1, $e); 1374*04fd306cSNickeau } finally { 1375*04fd306cSNickeau $executionContext->closeExecutingMarkupHandler(); 1376*04fd306cSNickeau } 1377*04fd306cSNickeau } 1378*04fd306cSNickeau 1379*04fd306cSNickeau public function getSnippetCachePath(): LocalPath 1380*04fd306cSNickeau { 1381*04fd306cSNickeau $cache = $this->getSnippetCacheStore()->cache; 1382*04fd306cSNickeau return LocalPath::createFromPathString($cache); 1383*04fd306cSNickeau 1384*04fd306cSNickeau } 1385*04fd306cSNickeau 1386*04fd306cSNickeau /** 1387*04fd306cSNickeau * @return string - an execution id to be sure that we don't execute the same twice in recursion 1388*04fd306cSNickeau */ 1389*04fd306cSNickeau public function getId(): string 1390*04fd306cSNickeau { 1391*04fd306cSNickeau 1392*04fd306cSNickeau return "({$this->getSourceName()}) to ({$this->builderName} - {$this->getMime()}) with context ({$this->getRequestedContextPath()->toUriString()})"; 1393*04fd306cSNickeau } 1394*04fd306cSNickeau 1395*04fd306cSNickeau /** 1396*04fd306cSNickeau * @return string - a name for the source (used in {@link self::__toString()} and {@link self::getId() identification}) 1397*04fd306cSNickeau */ 1398*04fd306cSNickeau private function getSourceName(): string 1399*04fd306cSNickeau { 1400*04fd306cSNickeau if (!$this->isPathExecution()) { 1401*04fd306cSNickeau if (isset($this->markupString)) { 1402*04fd306cSNickeau $md5 = md5($this->markupString); 1403*04fd306cSNickeau return "Markup String Execution ($md5)"; 1404*04fd306cSNickeau } elseif (isset($this->requestedInstructions)) { 1405*04fd306cSNickeau return "Markup Instructions Execution"; 1406*04fd306cSNickeau } else { 1407*04fd306cSNickeau LogUtility::internalError("The name of the markup handler is unknown"); 1408*04fd306cSNickeau return "Markup Unknown Execution"; 1409*04fd306cSNickeau 1410*04fd306cSNickeau } 1411*04fd306cSNickeau } else { 1412*04fd306cSNickeau try { 1413*04fd306cSNickeau return $this->getSourcePath()->toUriString(); 1414*04fd306cSNickeau } catch (ExceptionNotFound $e) { 1415*04fd306cSNickeau throw new ExceptionRuntimeInternal("A source path should be defined if it's not a markup string execution"); 1416*04fd306cSNickeau } 1417*04fd306cSNickeau } 1418*04fd306cSNickeau } 1419*04fd306cSNickeau 1420*04fd306cSNickeau 1421*04fd306cSNickeau /** 1422*04fd306cSNickeau * TODO: move to a FetcherMarkup by object type output and mime 1423*04fd306cSNickeau * @return array 1424*04fd306cSNickeau */ 1425*04fd306cSNickeau private function getProcessedInstructions(): array 1426*04fd306cSNickeau { 1427*04fd306cSNickeau 1428*04fd306cSNickeau 1429*04fd306cSNickeau if (!$this->shouldInstructionProcess()) { 1430*04fd306cSNickeau 1431*04fd306cSNickeau $this->processedInstructions = $this->instructionsCache->retrieveCache(); 1432*04fd306cSNickeau 1433*04fd306cSNickeau } else { 1434*04fd306cSNickeau 1435*04fd306cSNickeau $this->processInstructions(); 1436*04fd306cSNickeau 1437*04fd306cSNickeau } 1438*04fd306cSNickeau return $this->processedInstructions; 1439*04fd306cSNickeau 1440*04fd306cSNickeau } 1441*04fd306cSNickeau 1442*04fd306cSNickeau /** 1443*04fd306cSNickeau * @throws ExceptionNotFound 1444*04fd306cSNickeau */ 1445*04fd306cSNickeau public function getParent(): FetcherMarkup 1446*04fd306cSNickeau { 1447*04fd306cSNickeau if (!isset($this->parentMarkupHandler)) { 1448*04fd306cSNickeau throw new ExceptionNotFound(); 1449*04fd306cSNickeau } 1450*04fd306cSNickeau return $this->parentMarkupHandler; 1451*04fd306cSNickeau } 1452*04fd306cSNickeau 1453*04fd306cSNickeau 1454*04fd306cSNickeau} 1455