Passed
Push — master ( b9ff22...31f6ed )
by Enrico
01:47
created

AbstractModel::__construct()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7.0145

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 14
cts 15
cp 0.9333
rs 8.5386
c 0
b 0
f 0
cc 7
nc 9
nop 2
crap 7.0145
1
<?php
2
namespace BOTK\Model;
3
4
abstract class AbstractModel 
5
{
6
	
7
	/**
8
	 * 
9
	 * MUST be redefined by concrete class with the model schema 
10
	 * Each array element is composed by a propery name and and property options.
11
	 * Property option is an array with following (optional) fields:
12
	 * 		'default' 	a value to be used for the propery
13
	 * 		'filter' 	a php filter 
14
	 * 		'options' 	php filter options
15
	 * 		'flags'		php filter flags
16
	 * 
17
	 * Example:array (
18
	 *	'legalName'			=> array(
19
	 *							'filter'    => FILTER_CALLBACK,	
20
	 *	                        'options' 	=> '\BOTK\Filters::FILTER_NORMALIZZE_ADDRESS',
21
	 *		                   ),
22
	 *	'alternateName'		=> array(		
23
                            	'flags'  	=> FILTER_FORCE_ARRAY,
24
	 * 						),
25
	 * 	'postalCode'		=> array(	// italian rules
26
	 *							'filter'    => FILTER_VALIDATE_REGEXP,	
27
	 *	                        'options' 	=> array('regexp'=>'/^[0-9]{5}$/'),
28
	 *                      	'flags'  	=> FILTER_REQUIRE_SCALAR,
29
	 *		                   ),
30
	 * )
31
	 */
32
    protected static $DEFAULT_OPTIONS  = array(
33
        'base'				=> array(
34
            'default'	=> 'urn:resource:',
35
            'filter'    => FILTER_CALLBACK,
36
            'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
37
            'flags'  	=> FILTER_REQUIRE_SCALAR,
38
        ),
39
    );
40
    
41
    
42
	/**
43
	 * known vocabularies
44
	 */
45
	protected static $VOCABULARY  = array(
46
		'rdf'		=> 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
47
	    'xsd' 		=> 'http://www.w3.org/2001/XMLSchema#',
48
	);
49
	
50
51
	protected $options ;
52
	
53
	protected $data;
54
	protected $rdf =null; //lazy created
55
	protected $tripleCount=0; //lazy created
56
	protected $uniqueIdGenerator=null; // dependency injections
57
	protected $droppedFields = array();
58
	
59
60 12
	protected static function mergeOptions( array $options1, array $options2 )
61
	{
62 12
    	foreach($options2 as $property=>$option){
63
			
64 12
			$options1[$property]=isset($options1[$property])
65 5
				?array_merge($options1[$property], $option)
66 8
				:$option;
67
    	}
68
		
69 12
		return $options1;
70
	}
71
72
73 12
	protected static function constructOptions()
74
	{
75
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
76 12
		$thisClass = get_called_class();
77 12
		$parentClass = get_parent_class($thisClass);
78 12
		$exists = $parentClass?method_exists($parentClass, __FUNCTION__):false; 
79 12
		return $exists ? 
80 12
			self::mergeOptions($parentClass::constructOptions(), $thisClass::$DEFAULT_OPTIONS) : 
81 12
			$thisClass::$DEFAULT_OPTIONS;		
82
	}
83
84
85
	/**
86
	 * Do not call directlty constructor, use fromArray or other factory methodsinstead
87
	 */
88 12
    protected function __construct(array $data = array(), array $customOptions = array()) 
89
    { 		
90 12
 		$options = self::mergeOptions(self::constructOptions(),$customOptions);
91
		
92
		// set default values
93 12
		foreach( $options as $property=>$option){	
94 12
			if(empty($data[$property]) && isset($option['default'])){
95 10
				$data[$property] = $option['default'];
96
			}
97
		}
98
99
		// ensure data are sanitized and validated
100 12
		$sanitizedData = array_filter( filter_var_array($data, $options), function ($var){
101 12
		    return !is_null($var);
102 12
		});
103
			
104
		// find and register dropped fields
105 12
		foreach($data as $property=>$value){
106 12
			if($value && empty($sanitizedData[$property])){
107
				$this->droppedFields[]=$property;
108
			}
109
		}
110
111
112 12
		$this->options = $options;
113 12
		$this->data = $sanitizedData;
114 12
		$this->setIdGenerator(function($data){return uniqid();});
0 ignored issues
show
Unused Code introduced by
The parameter $data 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...
115 12
    }
116
	
117
	
118
	
119
	/**
120
	 * Create an instance from an associative array
121
	 */
122 12
	public static function fromArray(array $data, array $customOptions = array())
123
	{
124 12
		return new static($data,$customOptions);
125
	}
126
	
127
	
128
	/**
129
	 * Create an instance from an generic standard object
130
	 */
131 1
	public static function fromStdObject( \stdClass $obj, array $customOptions = array())
0 ignored issues
show
Unused Code introduced by
The parameter $customOptions 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...
132
	{
133 1
		return static::fromArray((array)$obj);
134
	}
135
136
137 4
	public static function getVocabularies()
138
	{
139
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
140 4
		$thisClass = get_called_class();
141 4
		$parentClass = get_parent_class($thisClass);
142 4
		$exists = $parentClass?method_exists($parentClass, __FUNCTION__):false; 
143 4
		return $exists ? 
144 4
			array_merge($parentClass::getVocabularies(), $thisClass::$VOCABULARY) : 
145 4
			$thisClass::$VOCABULARY;
146
	}
147
148
	
149 3
	public static function getTurtleHeader($base=null)
150
	{
151 3
		$vocabulariers = static::getVocabularies();
152 3
		$header = empty($base)?'': "@base <$base> .\n";
153 3
		foreach( $vocabulariers as $prefix=>$ns ){
154 3
	        $header.="@prefix $prefix: <$ns> .\n";
155
		}
156
		
157 3
		return $header;
158
	}
159
160
	
161
	public function getDroppedFields()
162
	{
163
		return $this->droppedFields;
164
	}
165
166
	
167
	/**
168
	 * dependecy injection setter 
169
	 */
170 12
	public function setIdGenerator($generator)
171
	{
172 12
		assert( is_callable($generator));
173 12
		$this->uniqueIdGenerator = $generator;
174
		
175 12
		return $this;
176
	}
177
178
179
	/**
180
	 * returns an uri
181
	 */
182 6
	public function getUri($id=null, $suffix='')
183
	{
184 6
	    assert(!empty($this->data['base'])) ;
185
	    
186 6
	    $base = (array)$this->data['base']; // base could be an array
187
	    
188 6
	    if (empty($id)) {
189 4
	        $idGenerator=$this->uniqueIdGenerator;
190 4
	        $id=$idGenerator($this->data);
191
	    }
192
193 6
	    return $base[0] . $id . $suffix;
194
	}
195
196
	
197 1
	public function getOptions()
198
	{
199 1
		return $this->options;
200
	}
201
202
203 1
	public function getTripleCount()
204
	{
205
		// triple count is computed during rdf creation
206 1
		if (!empty($this->data) && is_null($this->rdf)){
207 1
			$this->asTurtleFragment();
208
		}
209
		
210 1
		return $this->tripleCount;
211
	}
212
		
213
214 2
	public function asArray()
215
	{
216 2
		return $this->data;
217
	}	
218
219
220
221 1
	public function asStdObject()
222
	{
223 1
		return (object) $this->asArray();
224
	}
225
	
226
	
227
	/**
228
	 * metadata not yet implemented
229
	 */		
230 1
	public function asLinkedData() 
231
	{
232 1
	    $base = $this->data['base']?? null;
233 1
	    return $this->getTurtleHeader($base) ."\n". $this->asTurtleFragment();
234
	}
235
	
236
	
237 1
	public function asString() 
238
	{
239 1
		return $this->asLinkedData();
240
	}
241
	
242
		
243 1
	public function __toString() 
244
	{
245 1
		return $this->asString();
246
	}
247
248
249
	/**
250
	 * adds a turtle fragment managing cardinality > 1
251
	 */
252 1
	protected function addFragment($format, $var, $sanitize=true){
253 1
		foreach((array)$var as $v){
254 1
			if($var){
255 1
				$this->rdf .= sprintf($format, $sanitize?\BOTK\Filters::FILTER_SANITIZE_TURTLE_STRING($v):$v);
256 1
				$this->tripleCount++;
257
			}
258
		}
259 1
	}
260
261
	/**
262
	 * this must be implemented
263
	 */
264
	abstract public function asTurtleFragment();
265
}