PH7Tpl   F
last analyzed

Complexity

Total Complexity 82

Size/Duplication

Total Lines 862
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
dl 0
loc 862
rs 1.738
c 0
b 0
f 0
wmc 82
lcom 1
cbo 13

42 Methods

Rating   Name   Duplication   Size   Complexity  
A addSlashes() 0 4 1
A checkCompileDir() 0 6 2
A checkCacheDir() 0 6 2
A __construct() 0 15 1
A getMainPage() 0 4 1
A setTemplateDir() 0 10 2
A setCompileDir() 0 11 2
A setCacheDir() 0 10 2
A setCaching() 0 4 1
A isEnableCache() 0 4 1
A setHtmlCompress() 0 4 1
A setPhpCompress() 0 4 1
A setCacheExpire() 0 4 1
A __set() 0 4 1
A __get() 0 4 1
A __isset() 0 4 1
B display() 0 47 10
B parseMail() 0 53 6
A assign() 0 8 2
A assigns() 0 6 2
A getVar() 0 4 2
A clean() 0 4 1
A getReservedWords() 0 4 1
A getHeaderContents() 0 22 1
A __clone() 0 4 1
A cache() 0 35 4
A optimizeCode() 0 5 1
A getCurrentController() 0 4 1
B compile() 0 59 10
A parse() 0 14 1
A isMainPage() 0 4 1
A isMainCompilePage() 0 7 1
A isXmlSitemapCompilePage() 0 4 1
A isMainDir() 0 4 2
A checkDesignInstance() 0 4 2
A isMarkCopyright() 0 6 2
A isSmallMarkCopyright() 0 4 1
A notBaseTheme() 0 5 2
A hasCacheExpired() 0 6 3
A isKeywordFoundInCode() 0 4 1
A setErrMsg() 0 4 1
A __destruct() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like PH7Tpl often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PH7Tpl, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 34 and the first side effect is on line 20.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/***************************************************************************
3
 * @title            PH7 Template Engine
4
 * @desc             Template Engine with Compiler and Cache for pH7 CMS!
5
 *
6
 * @author           Pierre-Henry Soria <[email protected]>
7
 * @category         PH7 Template Engine
8
 * @package          PH7 / Framework / Layout / Tpl / Engine / PH7Tpl
9
 * @copyright        (c) 2011-2019, Pierre-Henry Soria. All Rights Reserved.
10
 * @version          1.4.0
11
 * @license          CC-BY License - http://creativecommons.org/licenses/by/3.0/
12
 *
13
 * @history          Supports now PHP 5 with beautiful object code (POO), (removed all the ugly object code from PHP 4.x).
14
 * @history          Supports now PHP 5.3 (added namespace and incorporate the template engine into the pH7Framework).
15
 * @history          Supports PHP 5.4 (added class member access on instantiation, e.g. (new Foo)->bar(), ...).
16
 ***************************************************************************/
17
18
namespace PH7\Framework\Layout\Tpl\Engine\PH7Tpl;
19
20
defined('PH7') or exit('Restricted access');
21
22
use PH7\Framework\Compress\Compress;
23
use PH7\Framework\Core\Kernel;
24
use PH7\Framework\Error\CException\PH7InvalidArgumentException;
25
use PH7\Framework\File\GenerableFile;
26
use PH7\Framework\Layout\Html\Design;
27
use PH7\Framework\Layout\Html\Mail as MailLayout;
28
use PH7\Framework\Layout\Tpl\Engine\PH7Tpl\Exception as TplException;
29
use PH7\Framework\Layout\Tpl\Engine\PH7Tpl\Syntax\Syntax;
30
use PH7\Framework\Layout\Tpl\Engine\Templatable;
31
use PH7\Framework\Mvc\Model\Design as DesignModel;
32
use PH7\Framework\Parse\SysVar;
33
34
class PH7Tpl extends Kernel implements Templatable, GenerableFile
35
{
36
    const NAME = 'PH7Tpl';
37
    const AUTHOR = 'Pierre-Henry Soria';
38
    const VERSION = '1.3.0';
39
    const LICENSE = 'Creative Commons Attribution 3.0 License - http://creativecommons.org/licenses/by/3.0/';
40
    const ERR_MSG = 'It seems you have removed the copyright notice(s) in the software. If you really want to remove them, please email: %s';
41
    const DATETIME_FORMAT = 'Y-m-d H:i:s';
42
43
    /**
44
     * @internal For better compatibility with Windows, we didn't put a slash at the end of the directory constants.
45
     */
46
    const COMPILE_DIR = 'pH7tpl_compile';
47
    const CACHE_DIR = 'pH7tpl_cache';
48
    const MAIN_COMPILE_DIR = 'public_main';
49
50
    const MAIN_PAGE = 'layout';
51
    const MAIN_COMPILE_PAGE = 'layout.cpl.php';
52
    const XML_SITEMAP_COMPILE_PAGE = 'mainlayout.xsl.cpl.php';
53
    const TEMPLATE_FILE_EXT = '.tpl';
54
    const COMPILE_FILE_EXT = '.cpl.php';
55
    const CACHE_FILE_EXT = '.cache.html';
56
57
    const RESERVED_WORDS = [
58
        'auto_include',
59
        'def_main_auto_include',
60
        'else',
61
        'literal',
62
        'lang'
63
    ];
64
65
    /** @var DesignModel */
66
    private $designModel;
67
68
    /** @var Syntax */
69
    private $oSyntaxEngine;
70
71
    /** @var string */
72
    private $sTplFile;
73
74
    /** @var string */
75
    private $sTemplateDir;
76
77
    /** @var string */
78
    private $sCompileDir;
79
80
    /** @var string */
81
    private $sCompileDir2;
82
83
    /** @var string */
84
    private $sCacheDir;
85
86
    /** @var string */
87
    private $sCacheDir2;
88
89
    /** @var string */
90
    private $sCode;
91
92
    /** @var string */
93
    private $sTemplateDirFile;
94
95
    /** @var string */
96
    private $sCompileDirFile;
97
98
    /** @var string */
99
    private $sCacheDirFile;
100
101
    /** @var bool */
102
    private $bCaching = false;
103
104
    /** @var bool */
105
    private $bHtmlCompressor;
106
107
    /** @var bool */
108
    private $bPhpCompressor;
109
110
    /** @var int|null */
111
    private $mCacheExpire;
112
113
    /** @var array */
114
    private $_aVars = [];
115
116
    /** @var PH7Tpl */
117
    private $_oVars;
118
119
    // Hack that keeps the $config variable in the template files
120
    protected $config;
121
122
    public function __construct(Syntax $oSyntaxEngine)
123
    {
124
        parent::__construct();
125
126
        $this->checkCompileDir();
127
        $this->checkCacheDir();
128
129
        /** Instance objects to the class **/
130
        $this->_oVars = $this;
131
        $this->designModel = new DesignModel;
132
        $this->oSyntaxEngine = new $oSyntaxEngine;
133
134
        $this->bHtmlCompressor = (bool)$this->config->values['cache']['enable.static.minify'];
135
        $this->bPhpCompressor = (bool)$this->config->values['cache']['enable.static.minify'];
136
    }
137
138
    /**
139
     * Get the main page file of the template.
140
     *
141
     * @return string The main page file.
142
     */
143
    public function getMainPage()
144
    {
145
        return static::MAIN_PAGE . static::TEMPLATE_FILE_EXT;
146
    }
147
148
    /**
149
     * Set the directory for the template.
150
     *
151
     * @param string $sDir
152
     *
153
     * @return void
154
     *
155
     * @throws PH7InvalidArgumentException An explanatory message if the directory does not exist.
156
     */
157
    public function setTemplateDir($sDir)
158
    {
159
        if (is_dir($sDir)) {
160
            $this->sTemplateDir = $this->file->checkExtDir($sDir);
161
        } else {
162
            throw new PH7InvalidArgumentException(
163
                sprintf('<strong>%s</strong> cannot find "%s" template directory.', self::NAME, $sDir)
164
            );
165
        }
166
    }
167
168
    /**
169
     * Set the directory for the compilation template.
170
     *
171
     * @param string $sDir
172
     *
173
     * @return void
174
     *
175
     * @throws PH7InvalidArgumentException An explanatory message if the directory does not exist.
176
     */
177
    public function setCompileDir($sDir)
178
    {
179
        if (is_dir($sDir)) {
180
            $this->sCompileDir = $this->file->checkExtDir($sDir);
181
        } else {
182
            throw new PH7InvalidArgumentException(
183
                sprintf(
184
                    '<strong>%s</strong> cannot find "%s" compile directory.', self::NAME, $sDir)
185
            );
186
        }
187
    }
188
189
    /**
190
     * Set the directory for the cache template.
191
     *
192
     * @param string $sDir
193
     *
194
     * @return void
195
     *
196
     * @throws PH7InvalidArgumentException An explanatory message if the directory does not exist.
197
     */
198
    public function setCacheDir($sDir)
199
    {
200
        if (is_dir($sDir)) {
201
            $this->sCacheDir = $this->file->checkExtDir($sDir);
202
        } else {
203
            throw new PH7InvalidArgumentException(
204
                sprintf('<strong>%s</strong> cannot find "%s" cache directory.', self::NAME, $sDir)
205
            );
206
        }
207
    }
208
209
    /**
210
     * Enabled the cache.
211
     *
212
     * @param bool $bCaching
213
     *
214
     * @return void
215
     */
216
    public function setCaching($bCaching)
217
    {
218
        $this->bCaching = (bool)$bCaching;
219
    }
220
221
    /**
222
     * Check if the cache is enabled.
223
     *
224
     * @return bool
225
     */
226
    public function isEnableCache()
227
    {
228
        return $this->bCaching;
229
    }
230
231
    /**
232
     * Set the HTML Compressor.
233
     *
234
     * @param bool $bCompressor
235
     *
236
     * @return void
237
     */
238
    public function setHtmlCompress($bCompressor)
239
    {
240
        $this->bHtmlCompressor = (bool)$bCompressor;
241
    }
242
243
    /**
244
     * Set the PHP Compressor.
245
     *
246
     * @param bool $bCompressor
247
     *
248
     * @return void
249
     */
250
    public function setPhpCompress($bCompressor)
251
    {
252
        $this->bPhpCompressor = (bool)$bCompressor;
253
    }
254
255
    /**
256
     * Set the time of expire cache.
257
     *
258
     * @param int $iLifeTime In seconds.
259
     *
260
     * @return void
261
     */
262
    public function setCacheExpire($iLifeTime)
263
    {
264
        $this->mCacheExpire = (int)$iLifeTime; // 3600 seconds = 1 hour cache duration
265
    }
266
267
    /**
268
     * Adds a variable that can be used by the templates.
269
     * Adds a new array index to the variable property. This
270
     * new array index will be treated as a variable by the templates.
271
     *
272
     * @see pH7Tpl::assign()
273
     *
274
     * @param string $sName The variable name to use in the template
275
     * @param mixed $mValue (string, object, array, integer, ...) Value Variable
276
     *
277
     * @return void
278
     */
279
    public function __set($sName, $mValue)
280
    {
281
        $this->assign($sName, $mValue);
282
    }
283
284
    /**
285
     * Retrieve an assigned variable (overload the magic __get method).
286
     *
287
     * @see pH7Tpl::getVar()
288
     *
289
     * @param string $sKey The variable name.
290
     *
291
     * @return mixed The variable value.
292
     */
293
    public function __get($sKey)
294
    {
295
        return $this->getVar($sKey);
296
    }
297
298
    /**
299
     * Allows testing with empty() and isset() to work.
300
     *
301
     * @param string $sKey
302
     *
303
     * @return bool
304
     */
305
    public function __isset($sKey)
306
    {
307
        return isset($this->_aVars[$sKey]);
308
    }
309
310
    /**
311
     * @param string $sTplFile Default NULL
312
     * @param string $sDirPath Default NULL
313
     * @param bool $bInclude Default TRUE
314
     *
315
     * @return string
316
     *
317
     * @throws TplException If the template file does no exist.
318
     * @throws PH7InvalidArgumentException
319
     */
320
    public function display($sTplFile = null, $sDirPath = null, $bInclude = true)
321
    {
322
        $this->sTplFile = $sTplFile;
323
324
        if (!empty($sDirPath)) {
325
            $this->setTemplateDir($sDirPath);
326
        }
327
328
        $this->sTemplateDirFile = $this->sTemplateDir . 'tpl' . PH7_DS . $this->sTplFile;
329
330
        $this->file->createDir($this->sCompileDir);
331
332
        if ($this->isMainDir($sDirPath)) {
333
            $this->sCompileDir2 = $this->sCompileDir . static::MAIN_COMPILE_DIR . PH7_DS . PH7_TPL_NAME . PH7_DS;
334
        } else {
335
            $this->sCompileDir2 = $this->sCompileDir . $this->registry->module . '_' . md5($this->registry->path_module) . PH7_DS . PH7_TPL_MOD_NAME . PH7_DS . $this->getCurrentController();
0 ignored issues
show
Documentation introduced by
The property module does not exist on object<PH7\Framework\Registry\Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property path_module does not exist on object<PH7\Framework\Registry\Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
336
        }
337
338
        $this->sCompileDirFile = ($this->isMainDir($sDirPath) ? $this->sCompileDir2 . $this->file->getFileWithoutExt($this->sTplFile) . static::COMPILE_FILE_EXT : $this->sCompileDir2) .
339
            str_replace($this->getCurrentController(), '', $this->file->getFileWithoutExt($this->sTplFile)) . static::COMPILE_FILE_EXT;
340
341
        if (!$this->file->existFile($this->sTemplateDirFile)) {
342
            throw new TplException(
343
                sprintf('%s file does no exist.', $this->sTemplateDirFile)
344
            );
345
        }
346
347
348
        /*** If the file does not exist or if the template has been modified, recompile the makefiles ***/
349
        if ($this->file->getModifTime($this->sTemplateDirFile) > $this->file->getModifTime($this->sCompileDirFile)) {
350
            $this->compile();
351
        }
352
353
        if ($bInclude) {
354
            $bCaching = (bool)$this->config->values['cache']['enable.html.tpl.cache'];
355
356
            if ($bCaching === true && $this->isEnableCache() === true && !$this->isMainCompilePage()) {
357
                $this->cache();
358
            } else {
359
                // Extraction Variables
360
                extract($this->_aVars);
361
                require $this->sCompileDirFile;
362
            }
363
        } else {
364
            return $this->sCompileDirFile;
365
        }
366
    }
367
368
    /**
369
     * Parse an email template.
370
     *
371
     * @param string $sMailTplFile
372
     * @param string $sEmailAddress It is used to create the privacy policy for lute against spam.
373
     *
374
     * @return string The contents of the template parsed.
375
     *
376
     * @throws TplException If the template file could not be opened.
377
     */
378
    public function parseMail($sMailTplFile, $sEmailAddress)
379
    {
380
        /**
381
         * If the template doesn't contain theme for emails, we retrieve the emails default themes.
382
         */
383
        if (!is_file($sMailTplFile) && defined('PH7_TPL_NAME')) {
384
            $sMailTplFile = str_replace(PH7_TPL_NAME, PH7_DEFAULT_THEME, $sMailTplFile);
385
        }
386
387
        if (!$sCode = $this->file->getFile($sMailTplFile)) {
388
            throw new TplException(
389
                sprintf('Cannot open "%s" file.', $sMailTplFile)
390
            );
391
        }
392
393
        /***** Other variables in file "/framework/Parse/SysVar.class.php" with syntax %var% *****/
394
        $sCode = (new SysVar)->parse($sCode);
0 ignored issues
show
Bug introduced by
It seems like $sCode can also be of type boolean; however, PH7\Framework\Parse\SysVar::parse() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
395
396
        foreach ($this->_aVars as $sKey => $sValue) {
397
            /*** Variables ***/
398
399
            // We can't convert an object to a string with str_replace, which we tested the variables with is_object function
400
            if (!is_object($sValue)) {
401
                $sCode = str_replace('{' . $sKey . '}', $sValue, $sCode);
402
            }
403
404
            // Email Address
405
            //$sCode = str_replace('{email}', $sEmailAddress, $sCode);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
406
407
            $oMailDesign = new MailLayout;
408
409
            /* Headers */
410
411
            // Includes
412
            $sCode = str_replace('{inc_header}', $oMailDesign->header(), $sCode);
413
            $sCode = str_replace('{inc_sub_header}', $oMailDesign->subHeader(), $sCode);
414
415
            /* Footers */
416
417
            // Privacy Policy Footer
418
            $sCode = str_replace('{pp_footer}', $oMailDesign->privacyPolicyFooter($sEmailAddress), $sCode);
419
420
            // Bottom Footer
421
            $sCode = str_replace('{b_footer}', $oMailDesign->bottomFooter(), $sCode);
422
423
            // Includes
424
            $sCode = str_replace('{inc_sub_footer}', $oMailDesign->subFooter($sEmailAddress), $sCode);
425
            $sCode = str_replace('{inc_footer}', $oMailDesign->footer(), $sCode);
426
            unset($oMailDesign);
427
        }
428
429
        return $sCode;
430
    }
431
432
    /**
433
     * Assign variables to the template.
434
     *
435
     *
436
     * @example
437
     *
438
     * Example with a string variable:
439
     *
440
     * <code>
441
     * === PHP ===
442
     *     $oPh7Tpl->assign('var_name', $sName);
443
     *
444
     * === TPL ===
445
     *     {var_name}
446
     * </code>
447
     *
448
     *
449
     * Example with an array variable:
450
     *
451
     * <code>
452
     * === PHP ===
453
     *     $oPh7Tpl->assign('arr_data_var', $aData);
454
     *
455
     * === TPL ===
456
     *     {% $arr_data_var['key1'] %}
457
     * </code>
458
     *
459
     *
460
     * Example with an object variable:
461
     *
462
     * <code>
463
     * === PHP ===
464
     *     $oPh7Tpl->assign('obj_user_var', $oUser);
465
     *
466
     * === TPL ===
467
     *     {% $obj_user_var->getUsers() %}
468
     * --- OR ---
469
     *      {{ $obj_user_var->printUsers() }}
470
     * </code>
471
     *
472
     *
473
     * @see __set()
474
     *
475
     * @param string $sName Variable name
476
     * @param mixed $mValue (string, object, array, integer, ...) Value Variable
477
     * @param bool $bEscape Specify "true" if you want to protect your variables against XSS.
478
     * @param bool $bEscapeStrip If you use escape method, you can also set this parameter to "true" to strip HTML and PHP tags from a string.
479
     *
480
     * @return void
481
     */
482
    public function assign($sName, $mValue, $bEscape = false, $bEscapeStrip = false)
483
    {
484
        if ($bEscape === true) {
485
            $mValue = $this->str->escape($mValue, $bEscapeStrip);
486
        }
487
488
        $this->_aVars[$sName] = $mValue;
489
    }
490
491
    /**
492
     * Assign variables from array.
493
     *
494
     * @see assign()
495
     *
496
     * @param array $aVars
497
     * @param bool $bEscape Specify TRUE if you want to protect your variables against XSS.
498
     * @param bool $bEscapeStrip If you use escape method, you can also set this parameter to "true" to strip HTML and PHP tags from a string.
499
     *
500
     * @return void
501
     */
502
    public function assigns(array $aVars, $bEscape = false, $bEscapeStrip = false)
503
    {
504
        foreach ($aVars as $sKey => $sValue) {
505
            $this->assign($sKey, $sValue, $bEscape, $bEscapeStrip); // Assign a string variable
506
        }
507
    }
508
509
    /**
510
     * Get a variable we assigned with the assign() method.
511
     *
512
     * @see __get()
513
     *
514
     * @param $sVarName string Name of a variable that is to be retrieved.
515
     *
516
     * @return mixed Value of that variable.
517
     */
518
    public function getVar($sVarName)
519
    {
520
        return isset($this->_aVars[$sVarName]) ? $this->_aVars[$sVarName] : '';
521
    }
522
523
    /**
524
     * Remove all variables from memory template.
525
     *
526
     * @return void
527
     */
528
    public function clean()
529
    {
530
        unset($this->_aVars, $this->_oVars);
531
    }
532
533
    /**
534
     * Get the reserved variables.
535
     *
536
     * @return array
537
     */
538
    public function getReservedWords()
539
    {
540
        return self::RESERVED_WORDS;
541
    }
542
543
    /**
544
     * Get the header content to put in the file.
545
     *
546
     * @return string
547
     */
548
    final public function getHeaderContents()
549
    {
550
        return '
551
namespace PH7;
552
defined(\'PH7\') or exit(\'Restricted access\');
553
/*
554
Created on ' . gmdate(self::DATETIME_FORMAT) . '
555
Compiled file from: ' . $this->sTemplateDirFile . '
556
Template Engine: ' . self::NAME . ' version ' . self::VERSION . ' by ' . self::AUTHOR . '
557
*/
558
/***************************************************************************
559
 *     ' . self::SOFTWARE_NAME . ' ' . self::SOFTWARE_COMPANY . '
560
 *               --------------------
561
 * @since      Mon Mar 21 2011
562
 * @author     SORIA Pierre-Henry
563
 * @email      ' . self::SOFTWARE_EMAIL . '
564
 * @link       ' . self::SOFTWARE_WEBSITE . '
565
 * @copyright  ' . sprintf(self::SOFTWARE_COPYRIGHT, date('Y')) . '
566
 * @license    ' . self::LICENSE . '
567
 ***************************************************************************/
568
';
569
    }
570
571
    /**
572
     * Set self pointer on cloned object.
573
     *
574
     * @clone
575
     */
576
    public function __clone()
577
    {
578
        $this->_oVars = $this;
579
    }
580
581
    /**
582
     * Cache system for the static contents with support for different templates and languages!
583
     *
584
     * @return void
585
     *
586
     * @throws Exception
587
     * @throws \PH7\Framework\File\Permission\PermissionException
588
     * @throws TplException If the cache file could not be written.
589
     */
590
    protected function cache()
591
    {
592
        // Create cache folder
593
        $this->file->createDir($this->sCacheDir);
594
595
        $this->sCacheDir2 = $this->sCacheDir . PH7_TPL_NAME . PH7_DS . $this->registry->module . '_' . md5($this->
0 ignored issues
show
Documentation introduced by
The property module does not exist on object<PH7\Framework\Registry\Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property path_module does not exist on object<PH7\Framework\Registry\Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
596
            registry->path_module) . PH7_DS . PH7_TPL_MOD_NAME . PH7_DS . PH7_LANG_NAME . PH7_DS . $this->getCurrentController() . PH7_DS;
597
        $this->file->createDir($this->sCacheDir2);
598
        $this->sCacheDirFile = $this->sCacheDir2 . str_replace(PH7_DS, '_', $this->file->getFileWithoutExt($this->sTplFile)) . static::CACHE_FILE_EXT;
599
600
        if ($this->hasCacheExpired()) {
601
            ob_start();
602
603
            // Extraction Variables
604
            extract($this->_aVars);
605
606
            require $this->sCompileDirFile;
607
            $sOutput = ob_get_contents();
608
            ob_end_clean();
609
610
            if ($this->bHtmlCompressor) {
611
                $sOutput = (new Compress)->parseHtml($sOutput);
612
            }
613
614
            if (!$this->file->putFile($this->sCacheDirFile, $sOutput)) {
615
                throw new TplException(
616
                    sprintf('Unable to write HTML cached file "%s"', $this->sCacheDirFile)
617
                );
618
            }
619
620
            echo $sOutput;
621
        } else {
622
            readfile($this->sCacheDirFile);
623
        }
624
    }
625
626
    /**
627
     * Optimizes the code generated by pH7Tpl syntax parser.
628
     *
629
     * @return void
630
     */
631
    protected function optimizeCode()
632
    {
633
        $this->sCode = preg_replace(['#[\t\r\n];?\?>#s', '#\?>[\t\r\n]+?<\?(php)?#si'], '', $this->sCode);
634
        $this->sCode = preg_replace('#;{2,}#s', ';', $this->sCode);
635
    }
636
637
    /**
638
     * Get current pH7CMS's controller.
639
     *
640
     * @return string The current controller
641
     */
642
    protected function getCurrentController()
643
    {
644
        return $this->httpRequest->currentController();
645
    }
646
647
    /**
648
     * Compiler template.
649
     *
650
     * @return bool
651
     *
652
     * @throws TplException If the template file could not be recovered or cannot be written.
653
     */
654
    final private function compile()
655
    {
656
        // Create compile folder
657
        $this->file->createDir($this->sCompileDir2);
658
659
        if (!$this->sCode = $this->file->getFile($this->sTemplateDirFile)) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->file->getFile($this->sTemplateDirFile) can also be of type boolean. However, the property $sCode is declared as type string. Maybe add an additional type check?

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 mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
660
            throw new TplException(
661
                sprintf('Impossible to fetch template file "%s"', $this->sTemplateDirFile)
662
            );
663
        }
664
665
        // Parser the predefined variables
666
        $this->sCode = (new Predefined\Variable($this->sCode))->assign()->get();
0 ignored issues
show
Bug introduced by
It seems like $this->sCode can also be of type boolean; however, PH7\Framework\Layout\Tpl...edefined::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
667
668
        // Parser the predefined template functions
669
        $this->sCode = (new Predefined\Func($this->sCode))->assign()->get();
670
671
        // Parser the language constructs
672
        $this->parse();
673
674
        $sPhpHeader = $this->getHeaderContents();
675
676
        // Check if the "$design" variable is actually part of the \PH7\Framework\Layout\Html\Design class
677
        if (!$this->checkDesignInstance()) {
678
            $this->setErrMsg();
679
        }
680
681
        /**
682
         * Skip this step if it's not layout.tpl file or if it's not the base template
683
         * (because there isn't "link()" in layout.tpl of other templates as it includes the "base" one).
684
         */
685
        if ($this->isMainCompilePage() && !$this->notBaseTheme()) {
686
            // It is forbidden to violate the copyright!
687
            // Think to me, who has spent years to develop a professional, high-quality software and done my best to help other developers!
688
            if (!$this->isMarkCopyright()) {
689
                $this->setErrMsg();
690
            }
691
        }
692
693
        if ($this->isXmlSitemapCompilePage() && !$this->isSmallMarkCopyright()) {
694
            $this->setErrMsg();
695
        }
696
697
        if ($this->bPhpCompressor) {
698
            $this->sCode = (new Compress)->parsePhp($this->sCode);
699
        }
700
701
        $this->sCode = '<?php ' . $sPhpHeader . '?>' . $this->sCode;
702
703
        if ($rHandle = @fopen($this->sCompileDirFile, 'wb')) {
704
            fwrite($rHandle, $this->sCode);
705
            fclose($rHandle);
706
            return true;
707
        }
708
709
        throw new TplException(
710
            sprintf('Could not write template compiled file "%s"', $this->sCompileDirFile)
711
        );
712
    }
713
714
    /**
715
     * Parse the template syntax code for translating the language template to PHP.
716
     *
717
     * @return void
718
     */
719
    private function parse()
720
    {
721
        $this->oSyntaxEngine->setCode($this->sCode);
722
        $this->oSyntaxEngine->setShortcutsToObjects();
723
724
        /***** Parse pH7Tpl's syntax *****/
725
        $this->oSyntaxEngine->setTemplateFile($this->sTplFile);
726
        $this->oSyntaxEngine->parse();
727
728
        $this->sCode = $this->oSyntaxEngine->getParsedCode();
729
730
        /***** Code optimization *****/
731
        $this->optimizeCode();
732
    }
733
734
    /**
735
     * Checks if the template file in the $this->sTemplateDirFile attribute is the main page (layout.tpl).
736
     *
737
     * @return bool
738
     */
739
    private function isMainPage()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
740
    {
741
        return preg_match('#' . $this->addSlashes(PH7_PATH_TPL . PH7_TPL_NAME . PH7_DS . $this->getMainPage()) . '#', $this->sTemplateDirFile);
742
    }
743
744
    /**
745
     * Checks if the compile file in the $this->sCompileDirFile attribute is the main page (layout.cpl.php).
746
     *
747
     * @return bool
748
     */
749
    final private function isMainCompilePage()
750
    {
751
        return preg_match(
752
            '#' . $this->addSlashes($this->sCompileDir . static::MAIN_COMPILE_DIR . PH7_DS . PH7_TPL_NAME . PH7_DS . static::MAIN_COMPILE_PAGE) . '#',
753
            $this->sCompileDirFile
754
        );
755
    }
756
757
    /**
758
     * Checks if the compile file in the $this->sCompileDirFile attribute is the XML (with XSL layout) Sitemap page (mainlayout.xsl.cpl.php).
759
     *
760
     * @return bool
761
     */
762
    final private function isXmlSitemapCompilePage()
763
    {
764
        return preg_match('#' . static::XML_SITEMAP_COMPILE_PAGE . '#', $this->sCompileDirFile);
765
    }
766
767
    /**
768
     * Checks if the directory passed by the argument of the method is the main directory.
769
     *
770
     * @param string $sDirPath
771
     *
772
     * @return bool
773
     */
774
    final private function isMainDir($sDirPath)
775
    {
776
        return !empty($sDirPath) && preg_match('#' . $this->addSlashes(PH7_PATH_TPL . PH7_TPL_NAME . PH7_DS) . '#', $sDirPath);
777
    }
778
779
    /**
780
     * Check that the variable "$design" is actually parts of the Design class.
781
     *
782
     * @return bool
783
     */
784
    final private function checkDesignInstance()
785
    {
786
        return !empty($this->_aVars['design']) && $this->_aVars['design'] instanceof Design;
787
    }
788
789
    /**
790
     * Checks if the marks licensing, copyright has not been removed.
791
     *
792
     * @return bool
793
     */
794
    final private function isMarkCopyright()
795
    {
796
        // "link()" and "softwareComment()" can never be removed
797
        return $this->isKeywordFoundInCode('design->link()') &&
798
            $this->isKeywordFoundInCode('design->softwareComment()');
799
    }
800
801
    /**
802
     * Checks if the small links copyright has not been removed.
803
     *
804
     * @return bool
805
     */
806
    final private function isSmallMarkCopyright()
807
    {
808
        return $this->isKeywordFoundInCode('design->smallLink()');
809
    }
810
811
    /**
812
     * Check if it's not the base theme.
813
     *
814
     * @return bool Returns TRUE if it's not the base theme, FALSE otherwise.
815
     */
816
    final private function notBaseTheme()
817
    {
818
        return strpos($this->sTemplateDir, PH7_PATH_TPL . PH7_DEFAULT_THEME . PH7_DS) === false &&
819
            $this->isKeywordFoundInCode('$this->display(\'' . $this->getMainPage() . '\', PH7_PATH_TPL . PH7_DEFAULT_THEME . PH7_DS)');
820
    }
821
822
    /**
823
     * @return bool Returns TRUE if the cache has expired, FALSE otherwise.
824
     */
825
    private function hasCacheExpired()
826
    {
827
        return
828
            $this->file->getModifTime($this->sCompileDirFile) > $this->file->getModifTime($this->sCacheDirFile) ||
829
            (!empty($this->mCacheExpire) && $this->file->getModifTime($this->sCacheDirFile) < time() - $this->mCacheExpire);
830
    }
831
832
    /**
833
     * Add slashes to avoid errors with "preg_replace()" with Windows' backslashes in directories.
834
     *
835
     * @param string $sStr
836
     *
837
     * @return string Escaped string
838
     */
839
    private function addSlashes($sStr)
840
    {
841
        return addslashes($sStr);
842
    }
843
844
    /**
845
     * Checks if the compile directory has been defined otherwise we create a default directory.
846
     *
847
     * If the folder compile does not exist, it creates a folder.
848
     *
849
     * @return self
850
     */
851
    private function checkCompileDir()
852
    {
853
        $this->sCompileDir = empty($this->sCompileDir) ? PH7_PATH_CACHE . static::COMPILE_DIR . PH7_DS : $this->sCompileDir;
854
855
        return $this;
856
    }
857
858
    /**
859
     * Checks if the cache directory has been defined otherwise we create a default directory.
860
     * If the folder cache does not exist, it creates a folder.
861
     *
862
     * @return self
863
     */
864
    private function checkCacheDir()
865
    {
866
        $this->sCacheDir = empty($this->sCacheDir) ? PH7_PATH_CACHE . static::CACHE_DIR . PH7_DS : $this->sCacheDir;
867
868
        return $this;
869
    }
870
871
    /**
872
     * @param string $sKeyword
873
     *
874
     * @return bool
875
     */
876
    private function isKeywordFoundInCode($sKeyword)
877
    {
878
        return strpos($this->sCode, $sKeyword) !== false;
879
    }
880
881
    /**
882
     * Set the error message.
883
     *
884
     * @return void
885
     */
886
    final private function setErrMsg()
887
    {
888
        $this->sCode = sprintf(static::ERR_MSG, self::SOFTWARE_EMAIL);
889
    }
890
891
    public function __destruct()
892
    {
893
        $this->clean();
894
    }
895
}
896