Completed
Pull Request — 2.0.x (#6)
by Andrew
02:09
created

CFSTemplateManager::ensureWriteableDirectory()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 12
rs 9.2
cc 4
eloc 7
nc 4
nop 1
1
<?php
2
/**
3
 * @author     Andrew Coulton <[email protected]>
4
 * @copyright  2015 inGenerator Ltd
5
 * @license    http://kohanaframework.org/license
6
 */
7
8
namespace Ingenerator\KohanaView\TemplateManager;
9
10
use Ingenerator\KohanaView\TemplateCompiler;
11
use Ingenerator\KohanaView\TemplateManager;
12
13
/**
14
 * Manages compilation of templates from view files located within the cascading file system. This allows extension
15
 * modules or applications to provide their own templates that are used in place of defaults provided by other modules.
16
 *
17
 * Templates will be dynamically compiled and cached to disk:
18
 *  * If the recompile_always option is TRUE, then once for every execution
19
 *  * If the recompile_always option is FALSE, then only if the compiled template does not yet exist
20
 *
21
 * @package Ingenerator\KohanaView\TemplateManager
22
 */
23
class CFSTemplateManager implements TemplateManager
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $cache_dir;
29
30
    /**
31
     * @var CFSWrapper
32
     */
33
    protected $cascading_files;
34
35
    /**
36
     * @var array
37
     */
38
    protected $compiled_paths = [];
39
40
    /**
41
     * @var TemplateCompiler
42
     */
43
    protected $compiler;
44
45
    /**
46
     * @var boolean
47
     */
48
    protected $recompile_always;
49
50
    /**
51
     * Valid options:
52
     * * cache_dir => the path where compiled templates will be cached
53
     * * recompile_always => whether to recompile each template on every execution,
54
     *
55
     * @param TemplateCompiler $compiler
56
     * @param array            $options
57
     * @param CFSWrapper       $cascading_files
58
     */
59
    public function __construct(TemplateCompiler $compiler, array $options, CFSWrapper $cascading_files = NULL)
60
    {
61
        $this->cascading_files  = $cascading_files ?: new CFSWrapper;
62
        $this->compiler         = $compiler;
63
        $this->cache_dir        = trim($options['cache_dir'], '/');
64
        $this->recompile_always = \Arr::get($options, 'recompile_always', FALSE);
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function getPath($template_name)
71
    {
72
        $compiled_path = $this->cache_dir.'/'.$template_name.'.php';
73
74
        if ($this->isCompileRequired($compiled_path)) {
75
            $source   = $this->requireSourceFileContent($template_name);
76
            $compiled = $this->compiler->compile($source);
77
            $this->writeFile($compiled_path, $compiled);
78
            $this->compiled_paths[$compiled_path] = TRUE;
79
        }
80
81
        return $compiled_path;
82
    }
83
84
    /**
85
     * @param string $compiled_path
86
     *
87
     * @return bool
88
     */
89
    protected function isCompileRequired($compiled_path)
90
    {
91
        if ($this->recompile_always AND ! isset($this->compiled_paths[$compiled_path])) {
1 ignored issue
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
92
            return TRUE;
93
        }
94
95
        return ! file_exists($compiled_path);
96
    }
97
98
    /**
99
     * @param string $template_name
100
     *
101
     * @return string
102
     */
103
    protected function requireSourceFileContent($template_name)
104
    {
105
        if ( ! $source_file = $this->cascading_files->find_file('views', $template_name)) {
106
            throw new \InvalidArgumentException("Cannot find template source file 'views/$template_name'");
107
        }
108
109
        return file_get_contents($source_file);
110
    }
111
112
    /**
113
     * @param string $compiled_path
114
     * @param string $compiled
115
     */
116
    protected function writeFile($compiled_path, $compiled)
117
    {
118
        $this->ensureWriteableDirectory(dirname($compiled_path));
119
        file_put_contents($compiled_path, $compiled);
120
    }
121
122
    /**
123
     * @param string $path
124
     */
125
    protected function ensureWriteableDirectory($path)
126
    {
127
        if (is_dir($path)) {
128
            if ( ! is_writeable($path)) {
129
                throw new \RuntimeException("Cannot write to compiled template path '$path'");
130
            }
131
        } else {
132
            if ( ! mkdir($path, 0777, TRUE)) {
133
                throw new \RuntimeException("Cannot create template cache directory in '$path'");
134
            }
135
        }
136
    }
137
138
}
139