Completed
Push — master ( 3204cd...9afd5c )
by Andrii
02:53
created

File::save()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2.0625
1
<?php
2
/**
3
 * Automation tool mixed with code generator for easier continuous development
4
 *
5
 * @link      https://github.com/hiqdev/hidev
6
 * @package   hidev
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hidev\base;
12
13
use hidev\helpers\FileHelper;
14
use hidev\helpers\Helper;
15
use Yii;
16
17
/**
18
 * A file to be processed with hidev.
19
 *
20
 * @property string $minimalPath path to minimal example file
21
 */
22
class File extends \yii\base\Object
23
{
24
    /**
25
     * @var Goal
26
     */
27
    public $goal;
28
29
    /**
30
     * @var file hanler: renderer and parser
31
     */
32
    protected $_handler;
33
34
    /**
35
     * @var string absolute path
36
     */
37
    protected $_path;
38
39
    /**
40
     * @var string directory
41
     */
42
    protected $_dirname;
43
44
    /**
45
     * @var string name with extension
46
     */
47
    protected $_basename;
48
49
    /**
50
     * @var string name only, without extension
51
     */
52
    protected $_filename;
53
54
    /**
55
     * @var string extension
56
     */
57
    protected $_extension;
58
59
    /**
60
     * @var array file stat
61
     */
62
    protected $_stat;
63
64
    /**
65
     * @var array possible types
66
     */
67
    public $types = [];
68
69
    /**
70
     * @var string type
71
     */
72
    public $type;
73
74
    /**
75
     * @var string template
76
     */
77
    public $template;
78
79
    /**
80
     * @var mixed data
81
     */
82
    protected $data;
83
84
    /**
85
     * @var string path to minimal example file
86
     */
87
    public $minimal;
88
89
    /**
90
     * Create file object.
91
     * @param string|array $path or config
92
     * @return File
93
     */
94
    public static function create($path)
95
    {
96
        $config = is_array($path) ? $path : compact('path');
97
        $config['class'] = get_called_class();
98
99
        return Yii::createObject($config);
100
    }
101
102
    /**
103
     * Create plain file (with plain handler).
104
     * @param string $path
105
     * @return File
106
     */
107
    public static function plain($path)
108
    {
109
        return Yii::createObject([
110
            'class' => get_called_class(),
111
            'type'  => 'plain',
112
            'path'  => $path,
113
        ]);
114
    }
115
116
    public function getMinimalPath()
117
    {
118
        return Yii::getAlias($this->minimal);
119
    }
120
121
    /**
122
     * @var array type to extension correspondance
123
     */
124
    protected static $_extension2type = [
125
        'json'      => 'json',
126
        'yml'       => 'yaml',  /// first one is preferred
127
        'yaml'      => 'yaml',
128
        'xml'       => 'xml',
129
        'xml.dist'  => 'xml',
130
    ];
131
132
    public function getExtensionByType($type)
133
    {
134
        static $type2extension;
135
        if ($type2extension === null) {
136
            foreach (static::$_extension2type as $e => $t) {
137
                if (!$type2extension[$t]) {
138
                    $type2extension[$t] = $e;
139
                }
140
            }
141
        }
142
143
        return $type2extension[$type];
144
    }
145
146 2
    public function getTypeByExtension($extension)
147
    {
148 2
        return isset(static::$_extension2type[$extension]) ? static::$_extension2type[$extension] : null;
149
    }
150
151 1
    public function findType()
152
    {
153 1
        if ($this->type === 'plain') {
154
            return $this->type;
155
        }
156
157 1
        return ($this->goal ? $this->goal->fileType : null) ?: static::getTypeByExtension($this->_extension) ?: 'template';
158
    }
159
160 1
    public function setPath($path)
161
    {
162
        $path             = Yii::getAlias($path);
163
        $info             = pathinfo($path);
164 1
        $this->_path      = $path;
0 ignored issues
show
Documentation Bug introduced by
It seems like $path can also be of type boolean. However, the property $_path 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...
165 1
        $this->_dirname   = isset($info['dirname']) ? $info['dirname'] : null;
166 1
        $this->_basename  = isset($info['basename']) ? $info['basename'] : null;
167 1
        $this->_filename  = isset($info['filename']) ? $info['filename'] : null;
168 1
        $this->_extension = isset($info['extension']) ? $info['extension'] : null;
169
        $this->type       = $this->findType();
170 1
    }
171
172
    public function getPath()
173
    {
174
        return $this->_path;
175
    }
176
177
    public function setBasename($basename)
178
    {
179
        $this->setPath($this->_dirname . '/' . $basename);
180
    }
181
182
    public function getBasename()
183
    {
184
        return $this->_basename;
185
    }
186
187
    public function setDirname($dirname)
188
    {
189
        $this->setPath($dirname . '/' . $this->_basename);
190
    }
191
192
    public function getDirname()
193
    {
194
        return $this->_dirname;
195
    }
196
197
    public function setFilename($filename)
198
    {
199
        $this->setPath($this->_dirname . '/' . $filename . '.' . $this->_extension);
200
    }
201
202
    public function getFilename()
203
    {
204
        return $this->_filename;
205
    }
206
207
    public function setExtension($extension)
208
    {
209
        $this->setPath($this->_dirname . '/' . $this->_filename . '.' . $extension);
210
    }
211
212
    public function getExtension()
213
    {
214
        return $this->_extension;
215
    }
216
217
    public function getCtype()
218
    {
219
        return Helper::id2camel($this->type);
220
    }
221
222
    /**
223
     * Save file.
224
     * @param mixed $data
225
     * @return bool true if file was changed
226
     */
227 3
    public function save($data = null)
228
    {
229 3
        if ($data !== null) {
230 3
            $this->data = $data;
231
        }
232
233
        return $this->getHandler()->renderPath($this->getPath(), $this->data);
234
    }
235
236
    public function write($content)
237
    {
238
        return $this->getHandler()->write($this->getPath(), $content);
239
    }
240
241
    public function load()
242
    {
243
        return $this->data = $this->getHandler()->parsePath($this->getPath(), $this->getMinimalPath());
244
    }
245
246
    public function read()
247
    {
248
        return $this->getHandler()->read($this->getPath());
249
    }
250
251
    public function readArray()
252
    {
253
        return $this->getHandler()->readArray($this->getPath());
254
    }
255
256 1
    public function getHandler()
257
    {
258
        if (!is_object($this->_handler)) {
259 1
            $this->_handler = Yii::createObject([
260
                'class'    => 'hidev\handlers\\' . $this->getCtype() . 'Handler',
261 1
                'template' => $this->template,
262 1
                'goal'     => $this->goal,
263
            ]);
264
        }
265
266
        return $this->_handler;
267
    }
268
269
    public static function file_exists($path)
270
    {
271
        return file_exists(Yii::getAlias($path));
272
    }
273
274
    public function exists()
275
    {
276
        return file_exists($this->getPath());
277
    }
278
279
    public function find(array $types = [])
280
    {
281
        if (empty($types)) {
282
            $types = $this->types;
283
        }
284
        foreach ($types as $type) {
285
            foreach (static::$_extension2type as $e => $t) {
286
                if ($t === $type) {
287
                    $this->setExtension($e);
288
                    if ($this->exists()) {
289
                        return true;
290
                    }
291
                }
292
            }
293
        }
294
295
        return false;
296
    }
297
298
    public function get($name)
299
    {
300
        return $this->data[$name];
301
    }
302
303
    public function getStat($field = null)
304
    {
305
        if ($this->_stat === null) {
306
            $this->_stat = stat($this->getPath());
307
        }
308
309
        return is_null($field) ? $this->_stat : $this->_stat[$field];
310
    }
311
312
    public function getUid()
313
    {
314
        return (string) $this->getStat(4);
315
    }
316
317 View Code Duplication
    public function getOwner()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
318
    {
319
        if (!isset($this->_stat['owner'])) {
320
            $this->_stat['owner'] = posix_getpwuid($this->getUid());
321
        }
322
323
        return $this->getStat('owner')['name'];
324
    }
325
326
    public function getGid()
327
    {
328
        return (string) $this->getStat(5);
329
    }
330
331 View Code Duplication
    public function getGroup()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
332
    {
333
        if (!isset($this->_stat['group'])) {
334
            $this->_stat['group'] = posix_getgrgid($this->getGid());
335
        }
336
337
        return $this->getStat('group')['name'];
338
    }
339
340
    public function getPermissions()
341
    {
342
        return static::formatOctal($this->getStat(2));
343
    }
344
345
    public static function formatOctal($value)
346
    {
347
        return substr(sprintf('%o', $value), -4);
348
    }
349
350
    public function chmod($value)
351
    {
352
        $value = is_int($value) ? static::formatOctal($value) : (string) $value;
353
        if ($value === $this->getPermissions()) {
354
            return;
355
        }
356
        $path = $this->getPath();
357
        passthru("chmod $value $path");
358
        Yii::warning("chmod $path '$value'", 'file');
359
    }
360
361
    public function chown($value)
362
    {
363
        $ownergroup = $this->getOwner() . ':' . $this->getGroup();
364
        if (in_array((string) $value, [$ownergroup, $this->getOwner(), $this->getUid()], true)) {
365
            return;
366
        }
367
        $path = $this->getPath();
368
        passthru("chown $value $path");
369
        Yii::warning("chown $path '$value'", 'file');
370
    }
371
372
    public function chgrp($value)
373
    {
374
        if (in_array((string) $value, [$this->getGroup(), $this->getGid()], true)) {
375
            return;
376
        }
377
        $path = $this->getPath();
378
        passthru("chgrp $value $path");
379
        Yii::warning("chgrp $path '$value'", 'file');
380
    }
381
382
    public function symlink($dest)
383
    {
384
        if (file_exists($dest)) {
385
            return true;
386
        }
387
        FileHelper::symlink($this->path, $dest);
0 ignored issues
show
Documentation introduced by
The property path does not exist on object<hidev\base\File>. 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...
388
        Yii::warning("Symlinked $this->path $dest", 'file');
0 ignored issues
show
Documentation introduced by
The property path does not exist on object<hidev\base\File>. 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...
389
    }
390
}
391