Completed
Push — master ( a4deca...b10287 )
by Ori
01:31
created

Schema::__construct()   D

Complexity

Conditions 9
Paths 21

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
nc 21
dl 0
loc 36
c 0
b 0
f 0
cc 9
eloc 25
nop 1
rs 4.909
1
<?php
2
3
namespace frictionlessdata\tableschema;
4
5
/**
6
 *  Table Schema representation.
7
 *  Loads and validates a Table Schema descriptor from a descriptor / path to file / url containing the descriptor.
8
 */
9
class Schema
10
{
11
    protected $DEFAULT_FIELD_CLASS = '\\frictionlessdata\\tableschema\\Fields\\StringField';
12
13
    /**
14
     * Schema constructor.
15
     *
16
     * @param mixed $descriptor
17
     *
18
     * @throws Exceptions\SchemaLoadException
19
     * @throws Exceptions\SchemaValidationFailedException
20
     */
21
    public function __construct($descriptor)
22
    {
23
        if (Utils::isJsonString($descriptor)) {
24
            // it's a json encoded string
25
            try {
26
                $this->descriptor = json_decode($descriptor);
27
            } catch (\Exception $e) {
28
                throw new Exceptions\SchemaLoadException($descriptor, null, $e->getMessage());
29
            }
30
        } elseif (is_string($descriptor)) {
31
            // it's a url or file path
32
            $descriptorSource = $descriptor;
33
            try {
34
                $descriptor = file_get_contents($descriptorSource);
35
            } catch (\Exception $e) {
36
                throw new Exceptions\SchemaLoadException(null, $descriptorSource, $e->getMessage());
37
            }
38
            try {
39
                $this->descriptor = json_decode($descriptor);
40
            } catch (\Exception $e) {
41
                throw new Exceptions\SchemaLoadException($descriptor, $descriptorSource, $e->getMessage());
42
            }
43
        } else {
44
            $this->descriptor = $descriptor;
45
        }
46
        if (is_array($this->descriptor)) {
47
            $this->descriptor = json_decode(json_encode($this->descriptor));
48
        }
49
        if (!is_object($this->descriptor())) {
50
            throw new Exceptions\SchemaLoadException($descriptor, null, 'descriptor must be an object');
51
        }
52
        $validationErrors = SchemaValidator::validate($this->descriptor());
53
        if (count($validationErrors) > 0) {
54
            throw new Exceptions\SchemaValidationFailedException($validationErrors);
55
        }
56
    }
57
58
    /**
59
     * loads and validates the given descriptor source (php object / string / path to file / url)
60
     * returns an array of validation error objects.
61
     *
62
     * @param mixed $descriptor
63
     *
64
     * @return array
65
     */
66
    public static function validate($descriptor)
67
    {
68
        try {
69
            new static($descriptor);
70
71
            return [];
72
        } catch (Exceptions\SchemaLoadException $e) {
73
            return [
74
                new SchemaValidationError(SchemaValidationError::LOAD_FAILED, $e->getMessage()),
75
            ];
76
        } catch (Exceptions\SchemaValidationFailedException $e) {
77
            return $e->validationErrors;
78
        }
79
    }
80
81
    /**
82
     * @return object
83
     */
84
    public function descriptor()
85
    {
86
        return $this->descriptor;
87
    }
88
89
    public function fullDescriptor()
90
    {
91
        $fullDescriptor = $this->descriptor();
92
        $fullFieldDescriptors = [];
93
        foreach ($this->fields() as $field) {
94
            $fullFieldDescriptors[] = $field->fullDescriptor();
95
        }
96
        $fullDescriptor->fields = $fullFieldDescriptors;
97
        $fullDescriptor->missingValues = $this->missingValues();
98
99
        return $fullDescriptor;
100
    }
101
102
    public function field($name)
103
    {
104
        $fields = $this->fields();
105
        if (array_key_exists($name, $fields)) {
106
            return $fields[$name];
107
        } else {
108
            throw new \Exception("unknown field name: {$name}");
109
        }
110
    }
111
112
    /**
113
     * @return Fields\BaseField[] array of field name => field object
114
     */
115
    public function fields()
116
    {
117
        if (empty($this->fieldsCache)) {
118
            foreach ($this->descriptor()->fields as $fieldDescriptor) {
119
                if (!array_key_exists('type', $fieldDescriptor)) {
120
                    $field = new $this->DEFAULT_FIELD_CLASS($fieldDescriptor);
121
                } else {
122
                    $field = Fields\FieldsFactory::field($fieldDescriptor);
123
                }
124
                $this->fieldsCache[$field->name()] = $field;
125
            }
126
        }
127
128
        return $this->fieldsCache;
129
    }
130
131
    public function missingValues()
132
    {
133
        return isset($this->descriptor()->missingValues) ? $this->descriptor()->missingValues : [''];
134
    }
135
136
    public function primaryKey()
137
    {
138
        $primaryKey = isset($this->descriptor()->primaryKey) ? $this->descriptor()->primaryKey : [];
139
140
        return is_array($primaryKey) ? $primaryKey : [$primaryKey];
141
    }
142
143
    public function foreignKeys()
144
    {
145
        $foreignKeys = isset($this->descriptor()->foreignKeys) ? $this->descriptor()->foreignKeys : [];
146
        foreach ($foreignKeys as &$foreignKey) {
147
            if (!is_array($foreignKey->fields)) {
148
                $foreignKey->fields = [$foreignKey->fields];
149
            }
150
            if (!is_array($foreignKey->reference->fields)) {
151
                $foreignKey->reference->fields = [$foreignKey->reference->fields];
152
            }
153
        }
154
155
        return $foreignKeys;
156
    }
157
158
    /**
159
     * @param mixed[] $row
160
     *
161
     * @return mixed[]
162
     *
163
     * @throws Exceptions\FieldValidationException
164
     */
165
    public function castRow($row)
166
    {
167
        $outRow = [];
168
        $validationErrors = [];
169
        foreach ($this->fields() as $fieldName => $field) {
170
            $value = array_key_exists($fieldName, $row) ? $row[$fieldName] : null;
171
            if (in_array($value, $this->missingValues())) {
172
                $value = null;
173
            }
174
            try {
175
                $outRow[$fieldName] = $field->castValue($value);
176
            } catch (Exceptions\FieldValidationException $e) {
177
                $validationErrors = array_merge($validationErrors, $e->validationErrors);
178
            }
179
        }
180
        if (count($validationErrors) > 0) {
181
            throw new Exceptions\FieldValidationException($validationErrors);
182
        }
183
184
        return $outRow;
185
    }
186
187
    /**
188
     * @param array $row
189
     *
190
     * @return SchemaValidationError[]
191
     */
192
    public function validateRow($row)
193
    {
194
        try {
195
            $this->castRow($row);
196
197
            return [];
198
        } catch (Exceptions\FieldValidationException $e) {
199
            return $e->validationErrors;
200
        }
201
    }
202
203
    public function save($filename)
204
    {
205
        file_put_contents($filename, json_encode($this->fullDescriptor()));
206
    }
207
208
    protected $descriptor;
209
    protected $fieldsCache = null;
210
}
211