Completed
Push — master ( 48e73f...b60bd4 )
by
unknown
28s queued 13s
created

BaseDatapackage::copy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace frictionlessdata\datapackage\Datapackages;
4
5
use frictionlessdata\datapackage\Factory;
6
use frictionlessdata\datapackage\Package;
7
use frictionlessdata\datapackage\Registry;
8
use frictionlessdata\datapackage\Utils;
9
use frictionlessdata\datapackage\Validators\DatapackageValidator;
10
use frictionlessdata\datapackage\Exceptions\DatapackageValidationFailedException;
11
use frictionlessdata\datapackage\Exceptions\DatapackageInvalidSourceException;
12
use ZipArchive;
13
14
abstract class BaseDatapackage implements \Iterator
15
{
16
17
    /**
18
     * BaseDatapackage constructor.
19
     *
20
     * @param object $descriptor
21
     * @param null|string $basePath
22
     *
23
     * @param bool $skipValidations
24
     *
25
     * @throws \frictionlessdata\datapackage\Exceptions\DatapackageValidationFailedException
26
     */
27
    public function __construct($descriptor, $basePath = null, $skipValidations = false)
28
    {
29
        $this->descriptor = $descriptor;
30
        $this->basePath = $basePath;
31
        $this->skipValidations = $skipValidations;
32
        if (!$this->skipValidations) {
33
            $this->revalidate();
34
        }
35
    }
36
37
    public static function create($name, $resources, $basePath = null)
38
    {
39
        $datapackage = new static((object) [
40
            'name' => $name,
41
            'resources' => [],
42
        ], $basePath, true);
43
        foreach ($resources as $resource) {
44
            $datapackage->addResource($resource);
0 ignored issues
show
Bug introduced by
The call to addResource() misses a required argument $resource.

This check looks for function calls that miss required arguments.

Loading history...
45
        }
46
47
        return $datapackage;
48
    }
49
50
    public function revalidate()
51
    {
52
        $this->rewind();
53
        $validationErrors = $this->datapackageValidate();
54
        if (count($validationErrors) > 0) {
55
            throw new DatapackageValidationFailedException($validationErrors);
56
        }
57
    }
58
59
    public static function handlesDescriptor($descriptor)
60
    {
61
        return static::handlesProfile(Registry::getDatapackageValidationProfile($descriptor));
62
    }
63
64
    /**
65
     * returns the descriptor as-is, without adding default values or normalizing.
66
     *
67
     * @return object
68
     */
69
    public function descriptor()
70
    {
71
        return $this->descriptor;
72
    }
73
74
    public function resources()
75
    {
76
        $resources = [];
77
        foreach ($this->descriptor->resources as $resourceDescriptor) {
78
            $resources[$resourceDescriptor->name] = $this->initResource($resourceDescriptor);
79
        }
80
81
        return $resources;
82
    }
83
84
    public function getResource($name)
85
    {
86
        foreach ($this->descriptor->resources as $resourceDescriptor) {
87
            if ($resourceDescriptor->name == $name) {
88
                return $this->initResource($resourceDescriptor);
89
            }
90
        }
91
        throw new \Exception("couldn't find matching resource with name =  '{$name}'");
92
    }
93
94
    public function addResource($name, $resource)
95
    {
96
        if (is_a($resource, 'frictionlessdata\\datapackage\\Resources\\BaseResource')) {
97
            $resource = $resource->descriptor();
98
        } else {
99
            $resource = Utils::objectify($resource);
100
        }
101
        $resource->name = $name;
102
        $resourceDescriptors = [];
103
        $gotMatch = false;
104
        foreach ($this->descriptor->resources as $resourceDescriptor) {
105
            if ($resourceDescriptor->name == $resource->name) {
106
                $resourceDescriptors[] = $resource;
107
                $gotMatch = true;
108
            } else {
109
                $resourceDescriptors[] = $resourceDescriptor;
110
            }
111
        }
112
        if (!$gotMatch) {
113
            $resourceDescriptors[] = $resource;
114
        }
115
        $this->descriptor->resources = $resourceDescriptors;
116
        if (!$this->skipValidations) {
117
            $this->revalidate();
118
        }
119
    }
120
121
    // TODO: remove this function and use the getResource / addResource directly (will need to modify a lot of tests code)
122
    public function resource($name, $resource = null)
123
    {
124
        if ($resource) {
125
            $this->addResource($name, $resource);
126
        } else {
127
            return $this->getResource($name);
128
        }
129
    }
130
131
    public function removeResource($name)
132
    {
133
        $resourceDescriptors = [];
134
        foreach ($this->descriptor->resources as $resourceDescriptor) {
135
            if ($resourceDescriptor->name != $name) {
136
                $resourceDescriptors[] = $resourceDescriptor;
137
            }
138
        }
139
        $this->descriptor->resources = $resourceDescriptors;
140
        if (!$this->skipValidations) {
141
            $this->revalidate();
142
        }
143
    }
144
145
    public function saveDescriptor($filename)
146
    {
147
        return file_put_contents($filename, json_encode($this->descriptor()));
148
    }
149
150
    // standard iterator functions - to iterate over the resources
151
    public function rewind()
152
    {
153
        $this->currentResourcePosition = 0;
154
    }
155
156
    public function current()
157
    {
158
        return $this->initResource($this->descriptor()->resources[$this->currentResourcePosition]);
159
    }
160
161
    public function key()
162
    {
163
        return $this->currentResourcePosition;
164
    }
165
166
    public function next()
167
    {
168
        ++$this->currentResourcePosition;
169
    }
170
171
    public function valid()
172
    {
173
        return isset($this->descriptor()->resources[$this->currentResourcePosition]);
174
    }
175
176
    /**
177
     * @param $zip_filename
178
     *
179
     * @throws \frictionlessdata\datapackage\Exceptions\DatapackageInvalidSourceException
180
     * @throws \Exception
181
     */
182
    public function save($zip_filename)
183
    {
184
        Package::isZipPresent();
185
        $zip = new ZipArchive();
186
187
        $packageCopy = $this->copy();
188
189
        $base = tempnam(sys_get_temp_dir(), 'datapackage-zip-');
190
        $files = [
191
            'datapackage.json' => $base.'datapackage.json',
192
        ];
193
        $ri = 0;
194
        foreach ($packageCopy as $resource) {
195
            if ($resource->isRemote()) {
196
                continue;
197
            }
198
199
            $resourceFiles = [];
200
            $fileNames = $resource->save($base.'resource-'.$ri);
201
            foreach ($fileNames as $fileName) {
202
                $relname = str_replace($base.'resource-'.$ri, '', $fileName);
203
                $files['resource-'.$ri.$relname] = $fileName;
204
                $resourceFiles[] = 'resource-'.$ri.$relname;
205
            }
206
            $resource->descriptor()->path = count($resourceFiles) == 1 ? $resourceFiles[0] : $resourceFiles;
207
            ++$ri;
208
        }
209
        $packageCopy->saveDescriptor($files['datapackage.json']);
210
211
        register_shutdown_function(function () use ($base) {
212
            Utils::removeDir($base);
213
        });
214
        if ($zip->open($zip_filename, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
215
            foreach ($files as $filename => $resource) {
216
                $zip->addFile($resource, $filename);
217
            }
218
            $zip->close();
219
        } else {
220
            throw new DatapackageInvalidSourceException('zip file could not be saved.');
221
        }
222
    }
223
224
    /**
225
     * Make a new Datapackage object that is a copy of the current Datapackage. Bypasses validation.
226
     * @return $this
227
     * @throws DatapackageValidationFailedException
228
     */
229
    protected function copy()
230
    {
231
        return new static($this->descriptor, $this->basePath, true);
232
    }
233
234
    protected $descriptor;
235
    protected $currentResourcePosition = 0;
236
    protected $basePath;
237
    protected $skipValidations = false;
238
239
    /**
240
     * called by the resources iterator for each iteration.
241
     *
242
     * @param object $descriptor
243
     *
244
     * @return \frictionlessdata\datapackage\Resources\BaseResource
245
     * @throws \frictionlessdata\datapackage\Exceptions\ResourceValidationFailedException
246
     */
247
    protected function initResource($descriptor)
248
    {
249
        return Factory::resource($descriptor, $this->basePath, $this->skipValidations);
250
    }
251
252
    protected function datapackageValidate()
253
    {
254
        return DatapackageValidator::validate($this->descriptor(), $this->basePath);
0 ignored issues
show
Bug introduced by
It seems like $this->basePath can also be of type string; however, frictionlessdata\datapac...seValidator::validate() does only seem to accept null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
255
    }
256
257
    protected static function handlesProfile($profile)
258
    {
259
        return false;
260
    }
261
}
262