Completed
Push — master ( 751bec...101b9a )
by David
11s
created

Loader::__construct()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 23
Code Lines 11

Duplication

Lines 8
Ratio 34.78 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 8
loc 23
rs 8.7972
cc 4
eloc 11
nc 6
nop 1
1
<?php
2
/**
3
 * Copyright (c) 2013-2017
4
 *
5
 * @category  Library
6
 * @package   Dwoo
7
 * @author    Jordi Boggiano <[email protected]>
8
 * @author    David Sanchez <[email protected]>
9
 * @copyright 2008-2013 Jordi Boggiano
10
 * @copyright 2013-2017 David Sanchez
11
 * @license   http://dwoo.org/LICENSE LGPLv3
12
 * @version   1.4.0
13
 * @date      2017-03-16
14
 * @link      http://dwoo.org/
15
 */
16
17
namespace Dwoo;
18
19
/**
20
 * Handles plugin loading and caching of plugins names/paths relationships.
21
 * This software is provided 'as-is', without any express or implied warranty.
22
 * In no event will the authors be held liable for any damages arising from the use of this software.
23
 */
24
class Loader implements ILoader
25
{
26
    /**
27
     * Stores the plugin directories.
28
     *
29
     * @see addDirectory
30
     * @var array
31
     */
32
    protected $paths = array();
33
34
    /**
35
     * Stores the plugins names/paths relationships
36
     * don't edit this on your own, use addDirectory.
37
     *
38
     * @see addDirectory
39
     * @var array
40
     */
41
    protected $classPath = array();
42
43
    /**
44
     * Path where class paths cache files are written.
45
     *
46
     * @var string
47
     */
48
    protected $cacheDir;
49
50
    /**
51
     * Path where builtin plugins are stored.
52
     *
53
     * @var string
54
     */
55
    protected $corePluginDir;
56
57
    /**
58
     * Loader constructor.
59
     *
60
     * @param $cacheDir
61
     */
62
    public function __construct($cacheDir)
63
    {
64
        $this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR . 'Plugins';
65
        $this->cacheDir      = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
66
67
        // include class paths or rebuild paths if the cache file isn't there
68
        $cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php';
69
70
        $chachedClassPath = null;
71
72 View Code Duplication
        if (file_exists($cacheFile)) {
0 ignored issues
show
Duplication introduced by DSanchez
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
74
            $chachedClassPath = unserialize(file_get_contents($cacheFile));
75
76
            if (is_array($chachedClassPath)) {
77
                $this->classPath = $chachedClassPath + $this->classPath;
78
            }
79
        }
80
81
        if (!is_array($chachedClassPath)) {
82
            $this->rebuildClassPathCache($this->corePluginDir, $cacheFile);
83
        }
84
    }
85
86
    /**
87
     * Rebuilds class paths, scans the given directory recursively and saves all paths in the given file.
88
     *
89
     * @param string         $path      the plugin path to scan
90
     * @param string|boolean $cacheFile the file where to store the plugin paths cache, it will be overwritten
91
     *
92
     * @throws Exception
93
     */
94
    protected function rebuildClassPathCache($path, $cacheFile)
95
    {
96
        $tmp = array();
97
        if ($cacheFile !== false) {
98
            $tmp             = $this->classPath;
99
            $this->classPath = array();
100
        }
101
102
        // iterates over all files/folders
103
        foreach (new \DirectoryIterator($path) as $fileInfo) {
104
            if (!$fileInfo->isDot()) {
105
                if ($fileInfo->isDir()) {
106
                    $this->rebuildClassPathCache($fileInfo->getPathname(), false);
107
                } else {
108
                    $this->classPath[$fileInfo->getBasename('.php')] = $fileInfo->getPathname();
109
                }
110
            }
111
        }
112
113
        // save in file if it's the first call (not recursed)
114
        if ($cacheFile !== false) {
115
            if (!file_put_contents($cacheFile, serialize($this->classPath), LOCK_EX)) {
116
                throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
117
            }
118
            $this->classPath += $tmp;
119
        }
120
    }
121
122
    /**
123
     * Loads a plugin file.
124
     *
125
     * @param string $class       the plugin name, without the `Plugin` prefix
126
     * @param bool   $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it
127
     *                            has just been added, defaults to true
128
     *
129
     * @throws Exception
130
     */
131
    public function loadPlugin($class, $forceRehash = true)
132
    {
133
        /**
134
         * An unknown class was requested (maybe newly added) or the
135
         * include failed so we rebuild the cache. include() will fail
136
         * with an uncatchable error if the file doesn't exist, which
137
         * usually means that the cache is stale and must be rebuilt,
138
         * so we check for that before trying to include() the plugin.
139
         */
140
        if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset
141
                ($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) {
142
            if ($forceRehash) {
143
                $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' .
144
                    Core::RELEASE_TAG . '.php');
145
                foreach ($this->paths as $path => $file) {
146
                    $this->rebuildClassPathCache($path, $file);
147
                }
148
                if (isset($this->classPath[$class])) {
149
                    include_once $this->classPath[$class];
150
                } elseif (isset($this->classPath[$class . 'Compile'])) {
151
                    include_once $this->classPath[$class . 'Compile'];
152
                } else {
153
                    throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
154
                }
155
            } else {
156
                throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
157
            }
158
        }
159
    }
160
161
    /**
162
     * Adds a plugin directory, the plugins found in the new plugin directory
163
     * will take precedence over the other directories (including the default
164
     * dwoo plugin directory), you can use this for example to override plugins
165
     * in a specific directory for a specific application while keeping all your
166
     * usual plugins in the same place for all applications.
167
     * TOCOM don't forget that php functions overrides are not rehashed so you
168
     * need to clear the classpath caches by hand when adding those.
169
     *
170
     * @param string $pluginDirectory the plugin path to scan
171
     *
172
     * @throws Exception
173
     */
174
    public function addDirectory($pluginDirectory)
175
    {
176
        $pluginDir = realpath($pluginDirectory);
177
        if (!$pluginDir) {
178
            throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory);
179
        }
180
        $cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'),
181
                strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php';
182
        $this->paths[$pluginDir] = $cacheFile;
183 View Code Duplication
        if (file_exists($cacheFile)) {
0 ignored issues
show
Duplication introduced by seldaek
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
            $classpath       = file_get_contents($cacheFile);
185
            $this->classPath = unserialize($classpath) + $this->classPath;
186
        } else {
187
            $this->rebuildClassPathCache($pluginDir, $cacheFile);
188
        }
189
    }
190
}
191