These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Alpha\Util\Extension; |
||
4 | |||
5 | use Alpha\Util\Config\ConfigProvider; |
||
6 | use Alpha\Util\Logging\Logger; |
||
7 | use Alpha\Util\Extension\Markdown; |
||
8 | use Alpha\View\Widget\Image; |
||
9 | use Alpha\Exception\AlphaException; |
||
10 | |||
11 | /** |
||
12 | * A facade class for the TCPDF library which is used to convert some HTML content provided by the |
||
13 | * Markdown library to a PDF file using FPDF. |
||
14 | * |
||
15 | * @since 1.0 |
||
16 | * |
||
17 | * @author John Collins <[email protected]> |
||
18 | * @license http://www.opensource.org/licenses/bsd-license.php The BSD License |
||
19 | * @copyright Copyright (c) 2017, John Collins (founder of Alpha Framework). |
||
20 | * All rights reserved. |
||
21 | * |
||
22 | * <pre> |
||
23 | * Redistribution and use in source and binary forms, with or |
||
24 | * without modification, are permitted provided that the |
||
25 | * following conditions are met: |
||
26 | * |
||
27 | * * Redistributions of source code must retain the above |
||
28 | * copyright notice, this list of conditions and the |
||
29 | * following disclaimer. |
||
30 | * * Redistributions in binary form must reproduce the above |
||
31 | * copyright notice, this list of conditions and the |
||
32 | * following disclaimer in the documentation and/or other |
||
33 | * materials provided with the distribution. |
||
34 | * * Neither the name of the Alpha Framework nor the names |
||
35 | * of its contributors may be used to endorse or promote |
||
36 | * products derived from this software without specific |
||
37 | * prior written permission. |
||
38 | * |
||
39 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||
40 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||
41 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
42 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
43 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
||
44 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
45 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||
46 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
47 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
48 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
49 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
||
50 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
51 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
52 | * </pre> |
||
53 | */ |
||
54 | class TCPDFFacade |
||
55 | { |
||
56 | /** |
||
57 | * The HTML-format content that we will render as a PDF. |
||
58 | * |
||
59 | * @var string |
||
60 | * |
||
61 | * @since 1.0 |
||
62 | */ |
||
63 | private $content; |
||
64 | |||
65 | /** |
||
66 | * The PDF object that will be generated from the Markdown HTML content. |
||
67 | * |
||
68 | * @var \Alpha\Util\Extension\TCPDF |
||
69 | * |
||
70 | * @since 1.0 |
||
71 | */ |
||
72 | private $pdf; |
||
73 | |||
74 | /** |
||
75 | * The business object that stores the content will be rendered to Markdown. |
||
76 | * |
||
77 | * @var \Alpha\Model\Article |
||
78 | * |
||
79 | * @since 1.0 |
||
80 | */ |
||
81 | private $article = null; |
||
82 | |||
83 | /** |
||
84 | * The auto-generated name of the PDF cache file for the article. |
||
85 | * |
||
86 | * @var string |
||
87 | * |
||
88 | * @since 1.0 |
||
89 | */ |
||
90 | private $PDFFilename; |
||
91 | |||
92 | /** |
||
93 | * The auto-generated name of the HTML cache file for the article generated by Markdown. |
||
94 | * |
||
95 | * @var string |
||
96 | * |
||
97 | * @since 1.0 |
||
98 | */ |
||
99 | private $HTMLFilename; |
||
100 | |||
101 | /** |
||
102 | * Trace logger. |
||
103 | * |
||
104 | * @var \Alpha\Util\Logging\Logger |
||
105 | * |
||
106 | * @since 1.0 |
||
107 | */ |
||
108 | private static $logger = null; |
||
109 | |||
110 | /** |
||
111 | * The constructor. |
||
112 | * |
||
113 | * @param \Alpha\Model\ActiveRecord $article the business object that stores the content will be rendered to Markdown |
||
114 | * |
||
115 | * @since 1.0 |
||
116 | */ |
||
117 | public function __construct($article) |
||
118 | { |
||
119 | self::$logger = new Logger('TCPDFFacade'); |
||
120 | self::$logger->debug('>>__construct()'); |
||
121 | |||
122 | $config = ConfigProvider::getInstance(); |
||
123 | |||
124 | $this->article = $article; |
||
0 ignored issues
–
show
|
|||
125 | |||
126 | $reflect = new \ReflectionClass($this->article); |
||
127 | $classname = $reflect->getShortName(); |
||
128 | |||
129 | $this->PDFFilename = $config->get('app.file.store.dir').'cache/pdf/'.$classname.'_'.$this->article->getID().'_'.$this->article->getVersion().'.pdf'; |
||
130 | $this->HTMLFilename = $config->get('app.file.store.dir').'cache/html/'.$classname.'_'.$this->article->getID().'_'.$this->article->getVersion().'.html'; |
||
131 | |||
132 | // first check the PDF cache |
||
133 | if ($this->checkPDFCache()) { |
||
134 | return; |
||
135 | } |
||
136 | |||
137 | if ($this->checkHTMLCache()) { |
||
138 | $this->loadHTMLCache(); |
||
139 | } else { |
||
140 | $this->content = $this->markdown($this->article->get('content', true)); |
||
141 | $this->HTMLCache(); |
||
142 | } |
||
143 | |||
144 | // Replace all instances of $attachURL in link tags to links to the ViewAttachment controller |
||
145 | $attachments = array(); |
||
146 | preg_match_all('/href\=\"\$attachURL\/.*\"/', $this->content, $attachments); |
||
147 | |||
148 | foreach ($attachments[0] as $attachmentURL) { |
||
149 | $start = mb_strpos($attachmentURL, '/'); |
||
150 | $end = mb_strrpos($attachmentURL, '"'); |
||
151 | $fileName = mb_substr($attachmentURL, $start+1, $end-($start+1)); |
||
152 | |||
153 | if (method_exists($this->article, 'getAttachmentSecureURL')) { |
||
154 | $this->content = str_replace($attachmentURL, 'href='.$this->article->getAttachmentSecureURL($fileName), $this->content); |
||
155 | } |
||
156 | } |
||
157 | |||
158 | // Handle image attachments |
||
159 | $attachments = array(); |
||
160 | preg_match_all('/\<img\ src\=\"\$attachURL\/.*\".*\>/', $this->content, $attachments); |
||
161 | |||
162 | foreach ($attachments[0] as $attachmentURL) { |
||
163 | $start = mb_strpos($attachmentURL, '/'); |
||
164 | $end = mb_strrpos($attachmentURL, '" alt'); |
||
165 | $fileName = mb_substr($attachmentURL, $start+1, $end-($start+1)); |
||
166 | |||
167 | if ($config->get('cms.images.widget')) { |
||
168 | // get the details of the source image |
||
169 | $path = $this->article->getAttachmentsLocation().'/'.$fileName; |
||
170 | $image_details = getimagesize($path); |
||
171 | $imgType = $image_details[2]; |
||
172 | if ($imgType == 1) { |
||
173 | $type = 'gif'; |
||
174 | } elseif ($imgType == 2) { |
||
175 | $type = 'jpg'; |
||
176 | } else { |
||
177 | $type = 'png'; |
||
178 | } |
||
179 | |||
180 | $img = new Image($path, $image_details[0], $image_details[1], $type, 0.95, false, (boolean)$config->get('cms.images.widget.secure')); |
||
181 | $this->content = str_replace($attachmentURL, $img->renderHTMLLink(), $this->content); |
||
182 | } else { |
||
183 | // render a normal image link to the ViewAttachment controller |
||
184 | if (method_exists($this->article, 'getAttachmentSecureURL')) { |
||
185 | $this->content = str_replace($attachmentURL, '<img src="'.$this->article->getAttachmentSecureURL($fileName).'">', $this->content); |
||
186 | } |
||
187 | } |
||
188 | } |
||
189 | |||
190 | $this->pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); |
||
191 | $this->pdf->SetCreator(PDF_CREATOR); |
||
192 | $this->pdf->SetAuthor($this->article->get('author')); |
||
193 | $this->pdf->SetTitle($this->article->get('title')); |
||
194 | $this->pdf->SetSubject($this->article->get('description')); |
||
195 | |||
196 | //set margins |
||
197 | $this->pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); |
||
198 | $this->pdf->SetHeaderMargin(PDF_MARGIN_HEADER); |
||
199 | $this->pdf->SetFooterMargin(PDF_MARGIN_FOOTER); |
||
200 | |||
201 | //set auto page breaks |
||
202 | $this->pdf->SetAutoPageBreak(true, PDF_MARGIN_BOTTOM); |
||
203 | |||
204 | //set image scale factor |
||
205 | $this->pdf->setImageScale(2.5); |
||
206 | |||
207 | // add a page |
||
208 | $this->pdf->AddPage(); |
||
209 | |||
210 | // add the title |
||
211 | $title = '<h1>'.$this->article->get('title').'</h1>'; |
||
212 | // add some custom footer info about the article |
||
213 | $footer = '<br><p>Article URL: <a href="'.$this->article->get('URL').'">'.$this->article->get('URL').'</a><br>Title: '.$this->article->get('title').'<br>Author: '.$this->article->get('author').'</p>'; |
||
214 | |||
215 | // write the title |
||
216 | self::$logger->debug('Writing the title ['.$title.'] to the PDF'); |
||
217 | $this->pdf->writeHTML(utf8_encode($title), true, false, true, false, ''); |
||
218 | // output the HTML content |
||
219 | self::$logger->debug('Writing the content ['.$this->content.'] to the PDF'); |
||
220 | $this->pdf->writeHTML(utf8_encode($this->content), true, false, true, false, ''); |
||
221 | // write the article footer |
||
222 | $this->pdf->writeHTML(utf8_encode($footer), true, false, true, false, ''); |
||
223 | self::$logger->debug('Writing the footer ['.$footer.'] to the PDF'); |
||
224 | |||
225 | // save this PDF to the cache |
||
226 | $this->pdf->Output($this->PDFFilename, 'F'); |
||
227 | |||
228 | self::$logger->debug('<<__construct()'); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * Facade method which will invoke our custom markdown class rather than the standard one. |
||
233 | * |
||
234 | * @param $text The markdown content to parse |
||
235 | * |
||
236 | * @since 1.0 |
||
237 | */ |
||
238 | private function markdown($text) |
||
239 | { |
||
240 | $config = ConfigProvider::getInstance(); |
||
241 | |||
242 | /* |
||
243 | * Initialize the parser and return the result of its transform method. |
||
244 | * |
||
245 | */ |
||
246 | static $parser; |
||
247 | |||
248 | if (!isset($parser)) { |
||
249 | $parser = new Markdown(); |
||
250 | } |
||
251 | |||
252 | /* |
||
253 | * Replace all instances of $sysURL in the text with the app.url setting from config |
||
254 | * |
||
255 | */ |
||
256 | $text = str_replace('$sysURL', $config->get('app.url'), $text); |
||
257 | |||
258 | // transform text using parser. |
||
259 | return $parser->transform($text); |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Fetter for the content. |
||
264 | * |
||
265 | * @return string HTML rendered the content |
||
266 | * |
||
267 | * @since 1.0 |
||
268 | */ |
||
269 | public function getContent() |
||
270 | { |
||
271 | return $this->content; |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * Saves the HTML generated by Markdown to the cache directory. |
||
276 | * |
||
277 | * @throws \Alpha\Exception\AlphaException |
||
278 | * |
||
279 | * @since 1.0 |
||
280 | */ |
||
281 | private function HTMLCache() |
||
282 | { |
||
283 | // check to ensure that the article is not transient before caching it |
||
284 | if ($this->article->getID() != '00000000000') { |
||
285 | $fp = fopen($this->HTMLFilename, 'w'); |
||
286 | if (!$fp) { |
||
287 | throw new AlphaException('Failed to open the cache file for writing, directory permissions my not be set correctly!'); |
||
288 | } else { |
||
289 | flock($fp, 2); // locks the file for writting |
||
290 | fwrite($fp, $this->content); |
||
291 | flock($fp, 3); // unlocks the file |
||
292 | fclose($fp); //closes the file |
||
293 | } |
||
294 | } |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Used to check the HTML cache for the article cache file. |
||
299 | * |
||
300 | * @return bool true if the file exists, false otherwise |
||
301 | * |
||
302 | * @since 1.0 |
||
303 | */ |
||
304 | private function checkHTMLCache() |
||
305 | { |
||
306 | return file_exists($this->HTMLFilename); |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Method to load the content of the cache file to the $content attribute of this object. |
||
311 | * |
||
312 | * @throws \Alpha\Exception\AlphaException |
||
313 | * |
||
314 | * @since 1.0 |
||
315 | */ |
||
316 | private function loadHTMLCache() |
||
317 | { |
||
318 | $fp = fopen($this->HTMLFilename, 'r'); |
||
319 | |||
320 | if (!$fp) { |
||
321 | throw new AlphaException('Failed to open the cache file for reading, directory permissions my not be set correctly!', 'loadHTMLCache()'); |
||
322 | } else { |
||
323 | $this->content = fread($fp, filesize($this->HTMLFilename)); |
||
324 | fclose($fp); //closes the file |
||
325 | } |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Used to check the PDF cache for the article cache file. |
||
330 | * |
||
331 | * @return bool true if the file exists, false otherwise |
||
332 | * |
||
333 | * @since 1.0 |
||
334 | */ |
||
335 | private function checkPDFCache() |
||
336 | { |
||
337 | return file_exists($this->PDFFilename); |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * Returns the raw PDF data stream. |
||
342 | * |
||
343 | * @return string |
||
344 | * |
||
345 | * @since 2.0 |
||
346 | */ |
||
347 | public function getPDFData() |
||
348 | { |
||
349 | // first load the file |
||
350 | $handle = fopen($this->PDFFilename, 'r'); |
||
351 | $data = fread($handle, filesize($this->PDFFilename)); |
||
352 | fclose($handle); |
||
353 | |||
354 | return $data; |
||
355 | } |
||
356 | } |
||
357 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.