Completed
Branch master (7d7b3f)
by Ori
01:38
created

Factory::validate()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 66
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 39
nc 9
nop 2
dl 0
loc 66
rs 6.4099
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace frictionlessdata\datapackage;
4
5
use frictionlessdata\datapackage\Datapackages\BaseDatapackage;
6
use frictionlessdata\datapackage\Resources\BaseResource;
7
8
/**
9
 * datapackage and resource have different classes depending on the corresponding profile
10
 * this factory interface allows to validate and create object instances without having to check the profile first.
11
 */
12
class Factory
13
{
14
    /**
15
     * how many lines to validate sample when validating data streams.
16
     */
17
    const VALIDATE_PEEK_LINES = 10;
18
19
    /**
20
     * load, validate and create a datapackage object
21
     * supports loading from the following sources:
22
     *  - native PHP object containing the descriptor
23
     *  - JSON encoded object
24
     *  - URL (must be in either 'http' or 'https' schemes)
25
     *  - local filesystem (POSIX) path.
26
     *
27
     * @param mixed       $source
28
     * @param null|string $basePath optional, required only if you want to use relative paths
29
     *
30
     * @return Datapackages\BaseDatapackage
31
     *
32
     * @throws Exceptions\DatapackageInvalidSourceException
33
     * @throws Exceptions\DatapackageValidationFailedException
34
     */
35
    public static function datapackage($source, $basePath = null)
36
    {
37
        $source = static::loadSource($source, $basePath);
38
        $descriptor = $source->descriptor;
39
        $basePath = $source->basePath;
40
        $datapackageClass = static::getDatapackageClass($descriptor);
41
        $datapackage = new $datapackageClass($descriptor, $basePath);
42
43
        return $datapackage;
44
    }
45
46
    /**
47
     * create a resource object.
48
     *
49
     * @param object      $descriptor
50
     * @param null|string $basePath
51
     * @param bool        $skipValidations
52
     *
53
     * @return Resources\BaseResource
54
     *
55
     * @throws Exceptions\ResourceValidationFailedException
56
     */
57
    public static function resource($descriptor, $basePath = null, $skipValidations = false)
58
    {
59
        $resourceClass = static::getResourceClass($descriptor);
60
        $resource = new $resourceClass($descriptor, $basePath, $skipValidations);
61
62
        return $resource;
63
    }
64
65
    /**
66
     * validates a given datapackage descriptor
67
     * will load all resources, and sample 10 lines of data from each data source.
68
     *
69
     * @param mixed       $source   datapackage source - same as in datapackage function
70
     * @param null|string $basePath same as in datapackage function
71
     *
72
     * @return Validators\DatapackageValidationError[]
73
     */
74
    public static function validate($source, $basePath = null)
75
    {
76
        $curResource = 1;
77
        $curLine = null;
78
        try {
79
            $datapackage = static::datapackage($source, $basePath);
80
            foreach ($datapackage as $resource) {
81
                $curLine = 1;
82
                foreach ($resource as $line) {
83
                    if ($curLine == self::VALIDATE_PEEK_LINES) {
84
                        break;
85
                    }
86
                    ++$curLine;
87
                }
88
                ++$curResource;
89
            }
90
            // no validation errors
91
            return [];
92
        } catch (Exceptions\DatapackageInvalidSourceException $e) {
93
            // failed to load the datapackage descriptor
94
            // return a list containing a single LOAD_FAILED validation error
95
            return [
96
                new Validators\DatapackageValidationError(
97
                    Validators\DatapackageValidationError::LOAD_FAILED, $e->getMessage()
98
                ),
99
            ];
100
        } catch (Exceptions\DatapackageValidationFailedException $e) {
101
            // datapackage descriptor failed validation - return the validation errors
102
            return $e->validationErrors;
103
        } catch (Exceptions\ResourceValidationFailedException $e) {
104
            // resource descriptor failed validation - return the validation errors
105
            return [
106
                new Validators\DatapackageValidationError(
107
                    Validators\DatapackageValidationError::RESOURCE_FAILED_VALIDATION,
108
                    [
109
                        'resource' => $curResource,
110
                        'validationErrors' => $e->validationErrors,
111
                    ]
112
                ),
113
            ];
114
        } catch (Exceptions\DataStreamOpenException $e) {
115
            // failed to open data stream
116
            return [
117
                new Validators\DatapackageValidationError(
118
                    Validators\DatapackageValidationError::DATA_STREAM_FAILURE,
119
                    [
120
                        'resource' => $curResource,
121
                        'line' => 0,
122
                        'error' => $e->getMessage(),
123
                    ]
124
                ),
125
            ];
126
        } catch (Exceptions\DataStreamValidationException $e) {
127
            // failed to validate the data stream
128
            return [
129
                new Validators\DatapackageValidationError(
130
                    Validators\DatapackageValidationError::DATA_STREAM_FAILURE,
131
                    [
132
                        'resource' => $curResource,
133
                        'line' => $curLine,
134
                        'error' => $e->getMessage(),
135
                    ]
136
                ),
137
            ];
138
        }
139
    }
140
141
    public static function registerDatapackageClass($datapackageClass)
142
    {
143
        static::$registeredDatapackageClasses[] = $datapackageClass;
144
    }
145
146
    public static function clearRegisteredDatapackageClasses()
147
    {
148
        static::$registeredDatapackageClasses = [];
149
    }
150
151
    /**
152
     * @param $descriptor
153
     *
154
     * @return BaseDatapackage::class
0 ignored issues
show
Documentation introduced by
The doc-type BaseDatapackage::class could not be parsed: Unknown type name "BaseDatapackage::class" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
155
     */
156 View Code Duplication
    public static function getDatapackageClass($descriptor)
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...
157
    {
158
        $datapackageClasses = array_merge(
159
            // custom classes
160
            static::$registeredDatapackageClasses,
161
            // core classes
162
            [
163
                "frictionlessdata\\datapackage\\Datapackages\TabularDatapackage",
164
                "frictionlessdata\\datapackage\\Datapackages\DefaultDatapackage",
165
            ]
166
        );
167
        $res = null;
168
        foreach ($datapackageClasses as $datapackageClass) {
169
            if (call_user_func([$datapackageClass, 'handlesDescriptor'], $descriptor)) {
170
                $res = $datapackageClass;
171
                break;
172
            }
173
        }
174
        if (!$res) {
175
            // not matched by any known classes
176
            $res = "frictionlessdata\\datapackage\\Datapackages\CustomDatapackage";
177
        }
178
179
        return $res;
180
    }
181
182
    public static function registerResourceClass($resourceClass)
183
    {
184
        static::$registeredResourceClasses[] = $resourceClass;
185
    }
186
187
    public static function clearRegisteredResourceClasses()
188
    {
189
        static::$registeredResourceClasses = [];
190
    }
191
192
    /**
193
     * @param $descriptor
194
     *
195
     * @return BaseResource::class
0 ignored issues
show
Documentation introduced by
The doc-type BaseResource::class could not be parsed: Unknown type name "BaseResource::class" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
196
     */
197 View Code Duplication
    public static function getResourceClass($descriptor)
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...
198
    {
199
        $descriptor = Utils::objectify($descriptor);
200
        $resourceClasses = array_merge(
201
            // custom classes
202
            static::$registeredResourceClasses,
203
            // core classes
204
            [
205
                'frictionlessdata\\datapackage\\Resources\\TabularResource',
206
                'frictionlessdata\\datapackage\\Resources\\DefaultResource',
207
            ]
208
        );
209
        $res = null;
210
        foreach ($resourceClasses as $resourceClass) {
211
            if (call_user_func([$resourceClass, 'handlesDescriptor'], $descriptor)) {
212
                $res = $resourceClass;
213
                break;
214
            }
215
        }
216
        if (!$res) {
217
            // not matched by any known classes
218
            $res = 'frictionlessdata\\datapackage\\Resources\\CustomResource';
219
        }
220
221
        return $res;
222
    }
223
224
    protected static $registeredDatapackageClasses = [];
225
    protected static $registeredResourceClasses = [];
226
227
    /**
228
     * allows extending classes to add custom sources
229
     * used by unit tests to add a mock http source.
230
     */
231
    protected static function normalizeHttpSource($source)
232
    {
233
        return $source;
234
    }
235
236
    /**
237
     * allows extending classes to add custom sources
238
     * used by unit tests to add a mock http source.
239
     */
240
    protected static function isHttpSource($source)
241
    {
242
        return Utils::isHttpSource($source);
243
    }
244
245
    /**
246
     * loads the datapackage descriptor from different sources
247
     * returns an object containing:
248
     *   - the datapackage descriptor as native php object
249
     *   - normalized basePath.
250
     *
251
     * @param $source
252
     * @param $basePath
253
     *
254
     * @return object
255
     *
256
     * @throws Exceptions\DatapackageInvalidSourceException
257
     */
258
    protected static function loadSource($source, $basePath)
259
    {
260
        if (is_object($source)) {
261
            $descriptor = $source;
262
        } elseif (is_string($source)) {
263
            if (Utils::isJsonString($source)) {
264
                try {
265
                    $descriptor = json_decode($source);
266
                } catch (\Exception $e) {
267
                    throw new Exceptions\DatapackageInvalidSourceException(
268
                        'Failed to load source: '.json_encode($source).': '.$e->getMessage()
269
                    );
270
                }
271
            } elseif (static::isHttpSource($source)) {
272
                try {
273
                    $descriptor = json_decode(file_get_contents(static::normalizeHttpSource($source)));
274
                } catch (\Exception $e) {
275
                    throw new Exceptions\DatapackageInvalidSourceException(
276
                        'Failed to load source: '.json_encode($source).': '.$e->getMessage()
277
                    );
278
                }
279
                // http sources don't allow relative paths, hence basePath should remain null
280
                $basePath = null;
281
            } else {
282
                // not a json string and not a url - assume it's a file path
283
                if (empty($basePath)) {
284
                    // no basePath
285
                    // - assume source is the absolute path of the file
286
                    // - set it's directory as the basePath
287
                    $basePath = dirname($source);
288
                } else {
289
                    // got a basePath
290
                    // - try to prepend it to the source and see if such a file exists
291
                    // - if not - assume it's an absolute path
292
                    $absPath = $basePath.DIRECTORY_SEPARATOR.$source;
293
                    if (file_exists($absPath)) {
294
                        $source = $absPath;
295
                    }
296
                }
297
                try {
298
                    $descriptor = json_decode(file_get_contents($source));
299
                } catch (\Exception $e) {
300
                    throw new Exceptions\DatapackageInvalidSourceException(
301
                        'Failed to load source: '.json_encode($source).': '.$e->getMessage()
302
                    );
303
                }
304
            }
305
        } else {
306
            throw new Exceptions\DatapackageInvalidSourceException(
307
                'Invalid source: '.json_encode($source)
308
            );
309
        }
310
311
        return (object) ['descriptor' => $descriptor, 'basePath' => $basePath];
312
    }
313
}
314