Passed
Push — master ( 8152a8...e98e8e )
by Enrico
02:27
created

AbstractModel::asStdObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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
        'uri'				=> array(
34
            'filter'    => FILTER_CALLBACK,
35
            'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
36
            'flags'  	=> FILTER_REQUIRE_SCALAR,
37
        ),
38
        'base'				=> array(
39
            'default'	=> 'urn:local:',
40
            'filter'    => FILTER_CALLBACK,
41
            'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
42
            'flags'  	=> FILTER_REQUIRE_SCALAR,
43
        ),
44
        'id'				=> array(
45
            'filter'    => FILTER_CALLBACK,
46
            'options' 	=> '\BOTK\Filters::FILTER_SANITIZE_ID',
47
            'flags'  	=> FILTER_REQUIRE_SCALAR,
48
        )
49
    );
50
    
51
    
52
	/**
53
	 * known vocabularies
54
	 */
55
	protected static $VOCABULARY  = array(
56
		'rdf'		=> 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
57
		'rdfs'		=> 'http://www.w3.org/2000/01/rdf-schema#',
58
		'owl'		=> 'http://www.w3.org/2002/07/owl#',
59
	    'xsd' 		=> 'http://www.w3.org/2001/XMLSchema#',
60
	    'dc'	    =>  'http://purl.org/dc/elements/1.1/',
61
		'dct' 		=> 'http://purl.org/dc/terms/',
62
		'void' 		=> 'http://rdfs.org/ns/void#',
63
		'prov' 		=> 'http://www.w3.org/ns/prov#',
64
		'sd'		=> 'http://www.w3.org/ns/sparql-service-description#',
65
		'schema'	=> 'http://schema.org/',
66
		'wgs' 		=> 'http://www.w3.org/2003/01/geo/wgs84_pos#',
67
		'foaf' 		=> 'http://xmlns.com/foaf/0.1/',
68
		'qb'		=> 'http://purl.org/linked-data/cube#',
69
		'daq'		=> 'http://purl.org/eis/vocab/daq#',
70
		'skos'		=> 'http://www.w3.org/2004/02/skos/core#',
71
		'kees'		=> 'http://linkeddata.center/kees/v1#',
72
	    'botk'		=> 'http://linkeddata.center/botk/v1#',
73
	    'oa'	    =>  'http://www.w3.org/ns/oa#',
74
	);
75
	
76
77
	protected $options ;
78
	
79
	protected $data;
80
	protected $rdf =null; //lazy created
81
	protected $tripleCount=0; //lazy created
82
	protected $uniqueIdGenerator=null; // dependency injections
83
	protected $droppedFields = array();
84
	
85
86 11
	protected static function mergeOptions( array $options1, array $options2 )
87
	{
88 11
    	foreach($options2 as $property=>$option){
89
			
90 11
			$options1[$property]=isset($options1[$property])
91 5
				?array_merge($options1[$property], $option)
92 7
				:$option;
93
    	}
94
		
95 11
		return $options1;
96
	}
97
98
99 11
	protected static function constructOptions()
100
	{
101
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
102 11
		$thisClass = get_called_class();
103 11
		$parentClass = get_parent_class($thisClass);
104 11
		$exists = method_exists($parentClass, __FUNCTION__); 
105 11
		return $exists ? 
106 11
			self::mergeOptions($parentClass::constructOptions(), $thisClass::$DEFAULT_OPTIONS) : 
107 11
			$thisClass::$DEFAULT_OPTIONS;		
108
	}
109
110
111
	/**
112
	 * Do not call directlty constructor, use fromArray or other factory methodsinstead
113
	 */
114 11
    protected function __construct(array $data = array(), array $customOptions = array()) 
115
    { 		
116 11
 		$options = self::mergeOptions(self::constructOptions(),$customOptions);
117
		
118
		// set default values
119 11
		foreach( $options as $property=>$option){	
120 11
			if(empty($data[$property]) && isset($option['default'])){
121 9
				$data[$property] = $option['default'];
122
			}
123
		}
124
125
		// ensure data are sanitized and validated
126 11
		$sanitizedData = array_filter( filter_var_array($data, $options));
127
		
128
		// find and register dropped fields
129 11
		foreach($data as $property=>$value){
130 11
			if($value && empty($sanitizedData[$property])){
131
				$this->droppedFields[]=$property;
132
			}
133
		}
134
135
136 11
		$this->options = $options;
137 11
		$this->data = $sanitizedData;
138
		$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...
139 11
    }
140
	
141
	
142
	
143
	/**
144
	 * Create an instance from an associative array
145
	 */
146 11
	public static function fromArray(array $data, array $customOptions = array())
147
	{
148 11
		return new static($data,$customOptions);
149
	}
150
	
151
	
152
	/**
153
	 * Create an instance from an generic standard object
154
	 */
155 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...
156
	{
157 1
		return static::fromArray((array)$obj);
158
	}
159
160
161 4
	public static function getVocabularies()
162
	{
163
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
164 4
		$thisClass = get_called_class();
165 4
		$parentClass = get_parent_class($thisClass);
166 4
		$exists = method_exists($parentClass, __FUNCTION__); 
167 4
		return $exists ? 
168 4
			array_merge($parentClass::getVocabularies(), $thisClass::$VOCABULARY) : 
169 4
			$thisClass::$VOCABULARY;
170
	}
171
172
	
173 3
	public static function getTurtleHeader($base=null)
174
	{
175 3
		$vocabulariers = static::getVocabularies();
176 3
		$header = empty($base)?'': "@base <$base> .\n";
177 3
		foreach( $vocabulariers as $prefix=>$ns ){
178 3
			$header.="@prefix $prefix: <$ns> .\n";
179
		}
180
		
181 3
		return $header;
182
	}
183
184
	
185
	public function getDroppedFields()
186
	{
187
		return $this->droppedFields;
188
	}
189
190
	
191
	/**
192
	 * dependecy injection setter 
193
	 */
194 11
	public function setIdGenerator($generator)
195
	{
196 11
		assert( is_callable($generator));
197 11
		$this->uniqueIdGenerator = $generator;
198
		
199 11
		return $this;
200
	}
201
202
203
	/**
204
	 * a generic implementation that use uri, base and id property (all optionals)
205
	 */
206 4
	public function getUri()
207
	{
208 4
		if(!empty($this->data['uri'])){
209 1
			$uri =  $this->data['uri'];
210 3
		} elseif(!empty($this->data['base'])) {
211 3
			$idGenerator=$this->uniqueIdGenerator;
212 3
			$uri = $this->data['base'];
213 3
			$uri.=empty($this->data['id'])?$idGenerator($this->data):$this->data['id'];
214
		} else{
215
			$idGenerator=$this->uniqueIdGenerator;
216
			$uri = 'urn:local:botk:'.$idGenerator($this->data);
217
		}
218
		
219 4
		return $uri;
220
	}
221
222
	
223 2
	public function getOptions()
224
	{
225 2
		return $this->options;
226
	}
227
228
229
	public function getTripleCount()
230
	{
231
		// triple count is computed during rdf creation
232
		if (!empty($this->data) && is_null($this->rdf)){
233
			$this->asTurtleFragment();
234
		}
235
		
236
		return $this->tripleCount;
237
	}
238
		
239
240 1
	public function asArray()
241
	{
242 1
		return $this->data;
243
	}	
244
245
246
247 1
	public function asStdObject()
248
	{
249 1
		return (object) $this->asArray();
250
	}
251
	
252
	
253
	/**
254
	 * metadata not yet implemented
255
	 */		
256 1
	public function asLinkedData() 
257
	{
258 1
		return $this->getTurtleHeader() ."\n". $this->asTurtleFragment();
259
	}
260
	
261
	
262 1
	public function asString() 
263
	{
264 1
		return $this->asLinkedData();
265
	}
266
	
267
		
268 1
	public function __toString() 
269
	{
270 1
		return $this->asString();
271
	}
272
273
274
	/**
275
	 * adds a turtle fragment managing cardinality > 1
276
	 */
277
	protected function addFragment($format, $var, $sanitize=true){
278
		foreach((array)$var as $v){
279
			if($var){
280
				$this->rdf .= sprintf($format, $sanitize?\BOTK\Filters::FILTER_SANITIZE_TURTLE_STRING($v):$v);
281
				$this->tripleCount++;
282
			}
283
		}
284
	}
285
286
	/**
287
	 * this must be implemented
288
	 */
289
	abstract public function asTurtleFragment();
290
}