Completed
Push — master ( 342ffa...2ca792 )
by Andrii
05:25
created

File::fileSave()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 19
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 28
rs 8.439
1
<?php
2
3
/*
4
 * HiPanel core package
5
 *
6
 * @link      https://hipanel.com/
7
 * @package   hipanel-core
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2014-2016, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hipanel\models;
13
14
use hipanel\base\Err;
15
use Yii;
16
use yii\helpers\FileHelper;
17
use yii\helpers\Url;
18
use yii\web\NotFoundHttpException;
19
use yii\web\Response;
20
use yii\web\UploadedFile;
21
22
class File extends \hiqdev\hiart\ActiveRecord
23
{
24
    const MD5 = '76303fjsq06mcr234nc379z32x48';
25
    const SALT = 'salt';
26
27
    /**
28
     * @var string the directory to store uploaded files. You may use path alias here.
29
     * If not set, it will use the "upload" subdirectory under the application runtime path.
30
     */
31
    public $path = '@runtime/upload';
32
33
    /**
34
     * @param $key
35
     * @return bool|string
36
     */
37
    public static function uploadPath($key)
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
38
    {
39
        return Yii::getAlias('@runtime/upload/');
40
    }
41
42
    /**
43
     * @return array
44
     */
45
    public function attributes()
46
    {
47
        return [
48
            'id',
49
            'ids',
50
            'object_id',
51
            'object_ids',
52
            'object',
53
            'seller',
54
            'sellers',
55
            'url',
56
            'filename',
57
            'author_id',
58
            'filepath',
59
            'extension',
60
        ];
61
    }
62
63
    /**
64
     * @param $name
65
     * @return string
66
     */
67
    public static function getHash($name)
68
    {
69
        return md5(self::MD5 . $name . self::SALT);
70
    }
71
72
    public static function filePath($file_id)
73
    {
74
        return Yii::getAlias('@runtime/upload/' . substr($file_id, -2, 2) . DIRECTORY_SEPARATOR . $file_id);
75
//        return implode('/', [self::fileDir($md5), $md5]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
76
    }
77
78
    public static function fileDir($md5)
79
    {
80
        return implode('/', ['var/files/tickets', substr($md5, 0, 2)]); // $GLOBALS['PRJ_DIR'],
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% 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...
81
    }
82
83
    /**
84
     * @return string
85
     */
86
    public static function getTempFolder()
87
    {
88
        return Yii::getAlias('@runtime/tmp');
89
    }
90
91
    /**
92
     * @param $temp_name
93
     * @return string
94
     */
95
    private static function getTmpUrl($temp_name)
96
    {
97
        $key = self::getHash($temp_name);
98
        return Url::to(['/file/temp-view', 'temp_file' => $temp_name, 'key' => $key], true);
99
    }
100
101
    protected function fileGet($rows, $params = [])
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
102
    {
103
        $input = $this->_prepareData($rows);
0 ignored issues
show
Documentation Bug introduced by
The method _prepareData does not exist on object<hipanel\models\File>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
104
        $response = http::fetchPost($this->api_url . 'fileGet', $input);
0 ignored issues
show
Documentation introduced by
The property api_url does not exist on object<hipanel\models\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...
105
        return $response;
106
    }
107
108
    /**
109
     * @param $file
110
     * @param null $file_id
111
     * @param null $object_id
112
     * @return array|bool
113
     */
114
    private static function get_file_from_site($file, $file_id = null, $object_id = null, $object_name = null, $render = true)
115
    {
116
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
117
        $mime_type = finfo_file($finfo, $file);
118
        finfo_close($finfo);
119
//        $data = file_get_contents($file);
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...
120
//        if (err::is($data))
121
//            return self::put_file_from_api($file_id, $object_id, $object_name, $render);
122
        if ($mime_type === 'text/plain') {
123
            $encoded = json_decode(file_get_contents($file), true);
124
            if (err::is($encoded)) {
125
                return self::get_file_from_api($file_id, $object_id, $object_name, $render);
0 ignored issues
show
Bug Compatibility introduced by
The expression self::get_file_from_api(...$object_name, $render); of type string|boolean adds the type string to the return on line 125 which is incompatible with the return type documented by hipanel\models\File::get_file_from_site of type array|boolean.
Loading history...
126
            }
127
        }
128
        if ($render) {
129
            return self::responseFile($file, $mime_type);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return self::responseFile($file, $mime_type); (string) is incompatible with the return type documented by hipanel\models\File::get_file_from_site of type array|boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
130
        } else {
131
            return $file;
132
        }
133
    }
134
135
    private static function responseFile($path, $content_type)
136
    {
137
        $response = Yii::$app->getResponse();
138
        $response->format = Response::FORMAT_RAW;
139
        $response->getHeaders()->add('content-type', $content_type);
140
        return file_get_contents($path);
141
    }
142
143
    private static function get_file_from_api($file_id, $object_id = null, $object_name = null, $render = true)
144
    {
145
        $data = self::perform('Get', ['id' => $file_id, 'object_id' => $object_id, 'object' => $object_name]);
146
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
147
        $mime_type = finfo_buffer($finfo, $data);
148
        finfo_close($finfo);
149
        $finalDestination = self::filePath($file_id);
150
        FileHelper::createDirectory(dirname($finalDestination));
151
        $file = file_put_contents($finalDestination, $data);
152
        if (!$file) {
153
            throw new NotFoundHttpException('File not found!');
154
        }
155
        if ($render) {
156
            return self::responseFile($finalDestination, $mime_type);
157
        } else {
158
            return $finalDestination;
159
        }
160
    }
161
162
    public static function renderFile($file_id, $object_id = null, $object_name = null, $render = true, $nocache = false)
163
    {
164
        if (is_file(self::filePath($file_id)) && !$nocache) {
165
            $res = self::get_file_from_site(self::filePath($file_id), $file_id, $object_id, $object_name, $render);
166
        } else {
167
            $res = self::get_file_from_api($file_id, $object_id, $object_name, $render);
168
        }
169
        return $res;
170
    }
171
172
    public static function fileSave(array $files)
173
    {
174
        $arr_ids = [];
175
        foreach ($files as $file) {
176
            if ($file instanceof UploadedFile) {
177
                // Move to temporary destination
178
                $tempDestination = self::getTempFolder() . DIRECTORY_SEPARATOR . uniqid() . '.' . $file->extension;
179
                FileHelper::createDirectory(dirname($tempDestination));
180
                $file->saveAs($tempDestination);
181
                // Prepare to final destination
182
                $url = self::getTmpUrl(basename($tempDestination));
183
                $response =  self::perform('Put', [
184
                    'url' => $url,
185
                    'filename' => basename($tempDestination),
186
                ]);
187
                $file_id = $arr_ids[] = $response['id'];
188
                $finalDestination = self::filePath($file_id);
189
                FileHelper::createDirectory(dirname($finalDestination));
190
                if (!rename($tempDestination, $finalDestination)) {
191
                    throw new \LogicException('rename function is not work');
192
                }
193
                if (is_file($tempDestination)) {
194
                    unlink($tempDestination);
195
                }
196
            }
197
        }
198
        return $arr_ids;
199
    }
200
}
201