Completed
Push — master ( 023690...819b2d )
by Sébastien
06:02 queued 10s
created

ZeroConfDriver   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 91.58%

Importance

Changes 10
Bugs 6 Features 0
Metric Value
wmc 39
c 10
b 6
f 0
lcom 1
cbo 11
dl 0
loc 261
ccs 87
cts 95
cp 0.9158
rs 8.2857

11 Methods

Rating   Name   Duplication   Size   Complexity  
B checkParams() 0 19 12
A getModelsConfigFile() 0 6 1
B getModelsDefinition() 0 29 6
B saveModelsDefinition() 0 20 5
A setDbAdapter() 0 5 1
A getDbAdapter() 0 4 1
A getMetadata() 0 12 3
A clearMetadataCache() 0 5 1
B __construct() 0 18 5
A getDefaultMetadata() 0 21 3
A setMetadata() 0 5 1
1
<?php
2
3
namespace Soluble\Normalist\Driver;
4
5
use Soluble\Normalist\Driver\Exception;
6
use Soluble\Schema\Source;
7
use Zend\Db\Adapter\Adapter;
8
use Zend\Config\Writer;
9
10
class ZeroConfDriver implements DriverInterface
11
{
12
    /**
13
     * @var Source\AbstractSource
14
     */
15
    protected $metadata;
16
17
    /**
18
     *
19
     * @var array
20
     */
21
    protected $params;
22
23
    /**
24
     *
25
     * @var array
26
     */
27
    protected $default_options = array(
28
        'alias' => 'default',
29
        'path' => null,
30
        'version' => 'latest',
31
        'schema' => null,
32
        'permissions' => 0666
33
    );
34
35
    /**
36
     *
37
     * @var array
38
     */
39
    protected static $metadataCache = array();
40
41
    /**
42
     * Underlying database adapter
43
     * @var Adapter
44
     */
45
    protected $adapter;
46
47
    /**
48
     * Construct a new Zero configuration driver
49
     *
50
     * $params allows you to specify the
51
     *   path    : where to store the model definition (default to sys_get_temp_dir())
52
     *   alias   : the alias to use when using multiple schemas, default: 'default'
53
     *   version : the version to use, default to 'latest'
54
     *   schema  : the database schema name, default to current adapter connection
55
     *   permissions: by default the model file is created with permission 0666
56
     *
57
     *
58
     * @param Adapter $adapter
59
     * @param array|Traversable $params [alias,path,version]
60
     * @throws Exception\ModelPathNotFoundException
61
     * @throws Exception\InvalidArgumentException
62
     */
63 151
    public function __construct(Adapter $adapter, $params = array())
64
    {
65 151
        $this->setDbAdapter($adapter);
66
67 151
        if (!is_array($params) && !$params instanceof \Traversable) {
68 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params parameter expects an array or Traversable object');
69 1
        }
70
71 151
        $this->params = array_merge($this->default_options, (array) $params);
72 151
        if ($this->params['path'] == '') {
73 151
            $this->params['path'] = sys_get_temp_dir();
74 151
        }
75 151
        if (!is_dir($this->params['path'])) {
76 1
            $path = (string) $this->params['path'];
77 1
            throw new Exception\ModelPathNotFoundException(__METHOD__ . " Model directory not found '" . $path . "'");
78
        }
79 151
        $this->checkParams();
80 151
    }
81
82
    /**
83
     * Checks model configuration params
84
     * @throws Exception\InvalidArgumentException
85
     *
86
     */
87 151
    protected function checkParams()
88
    {
89 151
        if (!is_string($this->params['alias']) || trim($this->params['alias']) == '') {
90 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params["alias"] parameter expects valid string');
91
        }
92 151
        if ($this->params['schema'] !== null &&
93 151
                (!is_string($this->params['schema']) || trim($this->params['schema']) == '')) {
94 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params["schema"] parameter expects valid string');
95
        }
96 151
        if (!is_scalar($this->params['version']) || trim($this->params['version']) == '') {
97 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params["version"] parameter expects valid scalar value');
98
        }
99 151
        if (!is_string($this->params['path']) || trim($this->params['path']) == '') {
100
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params["path"] parameter expects valid string value');
101
        }
102 151
        if ($this->params['permissions'] != '' && !is_scalar($this->params['permissions'])) {
103 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ' $params["permission"] parameter expects string|interger|octal value');
104
        }
105 151
    }
106
107
    /**
108
     * Return models configuration file
109
     * @return string
110
     */
111 17
    public function getModelsConfigFile()
112
    {
113 17
        $o = $this->params;
114 17
        $file = $o['path'] . DIRECTORY_SEPARATOR . 'normalist_zeroconf_cache_' . $o['alias'] . '_' . $o['version'] . '.php';
115 17
        return $file;
116
    }
117
118
    /**
119
     * Get models definition according to options
120
     *
121
     * @throws Exception\ModelFileNotFoundException
122
     * @throws Exception\ModelFileCorruptedException
123
     * @return array
124
     */
125 17
    public function getModelsDefinition()
126
    {
127 17
        $file = $this->getModelsConfigFile();
128 17
        if (!file_exists($file) || !is_readable($file)) {
129 6
            throw new Exception\ModelFileNotFoundException(__METHOD__ . " Model configuration file '$file' does not exists or not readable");
130
        }
131
132 15
        if (defined('HHVM_VERSION')) {
133
            // As an 'evil' workaround, waiting for hhvm to comply
134
            // see https://github.com/facebook/hhvm/issues/1447
135
            $definition = false;
136
            $file_content = file_get_contents($file);
137
            $file_content = trim(str_replace('<?php', '', $file_content));
138
            $file_content = trim(str_replace('return array(', '$definition = array(', $file_content));
139
            eval($file_content);
140
            
141
        } else {
142 15
            $definition = include $file;
143
        }
144
145 15
        if (!$definition) {
146
            throw new Exception\ModelFileCorruptedException(__METHOD__ . " Model configuration file '$file' cannot be included");
147
        }
148 15
        if (!is_array($definition)) {
149 1
            throw new Exception\ModelFileCorruptedException(__METHOD__ . " Model configuration file '$file' was included but is not a valid array");
150
        }
151
152 15
        return $definition;
153
    }
154
155
    /**
156
     * Save model definition
157
     *
158
     * @throws Exception\ModelFileNotWritableException
159
     * @param array $models_definition
160
     * @return DriverInterface
161
     */
162 6
    protected function saveModelsDefinition(array $models_definition)
163
    {
164 6
        $file = $this->getModelsConfigFile();
165 6
        if (file_exists($file) && !is_writable($file)) {
166 2
            throw new Exception\ModelFileNotWritableException(__METHOD__ . "Model configuration file '$file' cannot be overwritten, not writable.");
167
        }
168
169
        //$config = new Config($models_defintion, true);
170 6
        $writer = new Writer\PhpArray();
171 6
        $models_definition['normalist'] = array('model_version' => Metadata\NormalistModels::VERSION);
172 6
        $writer->toFile($file, $models_definition, $exclusiveLock = true);
173 6
        $perms = $this->params['permissions'];
174 6
        if ($perms != '') {
175 6
            if (decoct(octdec($perms)) == $perms) {
176 1
                $perms = octdec($perms);
177 1
            }
178 6
            chmod($file, $perms);
179 6
        }
180 6
        return $this;
181
    }
182
183
    /**
184
     * Set underlying database adapter
185
     *
186
     * @param Adapter $adapter
187
     * @return DriverInterface
188
     */
189 151
    protected function setDbAdapter(Adapter $adapter)
190
    {
191 151
        $this->adapter = $adapter;
192 151
        return $this;
193
    }
194
195
    /**
196
     * Get underlying database adapter
197
     *
198
     * @return Adapter
199
     */
200 132
    public function getDbAdapter()
201
    {
202 132
        return $this->adapter;
203
    }
204
205
    /**
206
     * Get internal metadata reader
207
     *
208
     * @return Source\AbstractSource
209
     */
210 146
    public function getMetadata()
211
    {
212 146
        $cache_key = md5(serialize($this->params));
213 146
        if (!array_key_exists($cache_key, self::$metadataCache)) {
214 18
            if ($this->metadata === null) {
215 17
                self::$metadataCache[$cache_key] = $this->getDefaultMetadata();
216 17
            } else {
217 1
                self::$metadataCache[$cache_key] = $this->metadata;
218
            }
219 18
        }
220 146
        return self::$metadataCache[$cache_key];
221
    }
222
223
    /**
224
     *
225
     * @return ZeroConfDriver
226
     */
227 20
    public function clearMetadataCache()
228
    {
229 20
        self::$metadataCache = array();
230 20
        return $this;
231
    }
232
233
    /**
234
     *
235
     * @return Metadata\NormalistModels
236
     */
237 17
    protected function getDefaultMetadata()
238
    {
239
        try {
240 17
            $model_definition = $this->getModelsDefinition();
241 17
        } catch (Exception\ExceptionInterface $e) {
242
            // means model definition does not exists
243
            // lets load it from the current connection
244 6
            if ($this->params['schema'] == '') {
245 3
                $schema = null;
246 3
            } else {
247 3
                $schema = $this->params['schema'];
248
            }
249 6
            $conn = $this->adapter->getDriver()->getConnection()->getResource();
250 6
            $md = new Source\Mysql\MysqlInformationSchema($conn, $schema);
251 6
            $model_definition = (array) $md->getSchemaConfig();
252
253
            // For later use we save the models definition
254 6
            $this->saveModelsDefinition($model_definition);
255
        }
256 17
        return new Metadata\NormalistModels($model_definition);
257
    }
258
259
    /**
260
     * Set internal metadata reader
261
     *
262
     * @param Source\AbstractSource $metadata
263
     * @return ZeroConfDriver
264
     */
265 1
    public function setMetadata(Source\AbstractSource $metadata)
266
    {
267 1
        $this->metadata = $metadata;
268 1
        return $this;
269
    }
270
}
271