1 <?php
2 
3 namespace ComboStrap;
4 
5 
6 class MarkupRenderer
7 {
8 
9     /**
10      * Setting the mime to instructions will just not do any render processing.
11      * Just parse tree/instructions processing if needed
12      */
13     public const INSTRUCTION_EXTENSION = "i";
14 
15     const CANONICAL = "markup:renderer";
16     const XHTML_RENDERER = "xhtml";
17     const DEFAULT_RENDERER = self::XHTML_RENDERER;
18     const METADATA_EXTENSION = "meta";
19     /**
20      * @var string source of the renderer is a markup (and not instructions)
21      */
22     private string $markupSource;
23     /**
24      * @var array source of the rendere is instructions
25      */
26     private array $instructionsSource;
27 
28     private Mime $requestedMime;
29 
30     /**
31      * @var mixed
32      */
33     private $cacheAfterRendering = true;
34     private string $renderer;
35 
36     /**
37      * @var WikiPath the context path
38      * May be null (ie markup rendering without any context such as webcode)
39      */
40     private WikiPath $requestedContextPath;
41 
42     /**
43      * @var ?Path the path from where the instructions/markup where created
44      * This is mandatory to add cache dependency informations
45      * and set the path that is executing
46      */
47     private ?Path $executingPath;
48 
49 
50     /**
51      * @param string $markup
52      * @param Path|null $executingPath - the source of the markup - may be null (case of webcode)
53      * @param WikiPath|null $contextPath - the requested markup path - may be null (case of webcode)
54      * @return MarkupRenderer
55      */
56     public static function createFromMarkup(string $markup, ?Path $executingPath, ?WikiPath $contextPath): MarkupRenderer
57     {
58         $markupRenderer = (new MarkupRenderer())
59             ->setMarkup($markup);
60         if ($executingPath != null) {
61             $markupRenderer->setRequestedExecutingPath($executingPath);
62         }
63         if ($contextPath != null) {
64             $markupRenderer->setRequestedContextPath($contextPath);
65         }
66         return $markupRenderer;
67 
68     }
69 
70     private function setMarkup(string $markup): MarkupRenderer
71     {
72         $this->markupSource = $markup;
73         return $this;
74     }
75 
76     public static function createFromMarkupInstructions($instructions, FetcherMarkup $fetcherMarkup): MarkupRenderer
77     {
78         return (new MarkupRenderer())
79             ->setInstructions($instructions)
80             ->setRequestedContextPath($fetcherMarkup->getRequestedContextPath())
81             ->setRequestedExecutingPath($fetcherMarkup->getExecutingPathOrNull());
82     }
83 
84     public static function createFromInstructions($instructions): MarkupRenderer
85     {
86         return (new MarkupRenderer())->setInstructions($instructions);
87     }
88 
89 
90     public function setRequestedMimeToInstruction(): MarkupRenderer
91     {
92         try {
93             $this->setRequestedMime(Mime::createFromExtension(self::INSTRUCTION_EXTENSION));
94         } catch (ExceptionNotFound $e) {
95             throw new ExceptionRuntime("Internal error: the mime is internal and should be good");
96         }
97         return $this;
98 
99     }
100 
101     public function setRequestedMime(Mime $requestedMime): MarkupRenderer
102     {
103         $this->requestedMime = $requestedMime;
104         return $this;
105     }
106 
107     public function getOutput()
108     {
109 
110         $extension = $this->requestedMime->getExtension();
111         switch ($extension) {
112             case self::INSTRUCTION_EXTENSION:
113                 /**
114                  * Get the instructions adapted from {@link p_cached_instructions()}
115                  *
116                  * Note that this code may not run at first rendering
117                  *
118                  * Why ?
119                  * Because dokuwiki asks first page information
120                  * via the {@link pageinfo()} method.
121                  * This function then render the metadata (ie {@link p_render_metadata()} and therefore will trigger
122                  * the rendering with this function
123                  * ```p_cached_instructions(wikiFN($id),false,$id)```
124                  *
125                  * The best way to manipulate the instructions is not before but after
126                  * the parsing. See {@link \action_plugin_combo_instructionspostprocessing}
127                  *
128                  */
129                 return p_get_instructions($this->markupSource);
130 
131             default:
132                 /**
133                  * The code below is adapted from {@link p_cached_output()}
134                  * $ret = p_cached_output($file, 'xhtml', $pageid);
135                  */
136                 if (!isset($this->instructionsSource)) {
137                     $executingPath = null;
138                     if (isset($this->executingPath)) {
139                         $executingPath = $this->executingPath;
140                     }
141 
142                     $contextPath = null;
143                     if (isset($this->requestedContextPath)) {
144                         $contextPath = $this->requestedContextPath;
145                     }
146 
147                     $this->instructionsSource = MarkupRenderer::createFromMarkup($this->markupSource, $executingPath, $contextPath)
148                         ->setRequestedMimeToInstruction()
149                         ->getOutput();
150                 }
151 
152                 /**
153                  * Render
154                  */
155                 $result = p_render($this->getRendererNameOrDefault(), $this->instructionsSource, $info);
156                 $this->cacheAfterRendering = $info['cache'] !== null ? $info['cache'] : false;
157                 return $result;
158 
159         }
160 
161 
162     }
163 
164     private function setInstructions(array $instructions): MarkupRenderer
165     {
166         $this->instructionsSource = $instructions;
167         return $this;
168     }
169 
170 
171     function getRendererNameOrDefault(): string
172     {
173         if (isset($this->renderer)) {
174             return $this->renderer;
175         }
176         /**
177          * Note: This value is passed to {@link p_get_renderer} to get the renderer class
178          */
179         return self::DEFAULT_RENDERER;
180     }
181 
182     public function setRendererName(string $rendererName): MarkupRenderer
183     {
184         $this->renderer = $rendererName;
185         return $this;
186     }
187 
188     public function getCacheAfterRendering()
189     {
190         return $this->cacheAfterRendering;
191     }
192 
193     /**
194      * The page context in which this markup was requested
195      * @param WikiPath $path
196      * @return $this
197      */
198     public function setRequestedContextPath(WikiPath $path): MarkupRenderer
199     {
200         $this->requestedContextPath = $path;
201         return $this;
202     }
203 
204     public function setRequestedMimeToXhtml(): MarkupRenderer
205     {
206         try {
207             return $this->setRequestedMime(Mime::createFromExtension("xhtml"));
208         } catch (ExceptionNotFound $e) {
209             throw new ExceptionRuntime("Internal error", 0, $e);
210         }
211     }
212 
213     /**
214      * @throws ExceptionNotFound
215      */
216     private function getRequestedContextPath(): WikiPath
217     {
218 
219         if (!isset($this->requestedContextPath)) {
220             throw new ExceptionNotFound("No requested context path");
221         }
222         return $this->requestedContextPath;
223 
224     }
225 
226     public function setRequestedExecutingPath(?Path $executingPath): MarkupRenderer
227     {
228         $this->executingPath = $executingPath;
229         return $this;
230     }
231 
232 
233 }
234