RApiHalDocumentResource::_addLink()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Api
5
 *
6
 * @copyright   Copyright (C) 2008 - 2021 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
defined('JPATH_BASE') or die;
11
12
/**
13
 * Object to represent a hypermedia resource in HAL.
14
 *
15
 * @since  1.2
16
 */
17
class RApiHalDocumentResource extends RApiHalDocumentBase
18
{
19
	/**
20
	 * Json option
21
	 */
22
	const JSON_NUMERIC_CHECK_ON = true;
23
24
	/**
25
	 * Json option
26
	 */
27
	const JSON_NUMERIC_CHECK_OFF = false;
28
29
	/**
30
	 * @var bool
31
	 */
32
	protected $jsonNumericCheck = self::JSON_NUMERIC_CHECK_OFF;
33
34
	/**
35
	 * Internal storage of `RApiHalDocumentLink` objects
36
	 * @var array
37
	 */
38
	protected $_links = array();
39
40
	/**
41
	 * Internal storage of primitive types
42
	 * @var array
43
	 */
44
	protected $_data = array();
45
46
	/**
47
	 * Internal storage of `Resource` objects
48
	 * @var array
49
	 */
50
	protected $_embedded = array();
51
52
	/**
53
	 * Constructor.
54
	 *
55
	 * @param   string       $href      Href
56
	 * @param   array        $data      Data
57
	 * @param   string|null  $title     Title
58
	 * @param   string|null  $name      Name
59
	 * @param   string|null  $hreflang  Href language
60
	 */
61
	public function __construct($href, array $data = array(), $title = null, $name = null, $hreflang = null)
0 ignored issues
show
Unused Code introduced by
The parameter $title is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

61
	public function __construct($href, array $data = array(), /** @scrutinizer ignore-unused */ $title = null, $name = null, $hreflang = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $name is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

61
	public function __construct($href, array $data = array(), $title = null, /** @scrutinizer ignore-unused */ $name = null, $hreflang = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $href is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

61
	public function __construct(/** @scrutinizer ignore-unused */ $href, array $data = array(), $title = null, $name = null, $hreflang = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $hreflang is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

61
	public function __construct($href, array $data = array(), $title = null, $name = null, /** @scrutinizer ignore-unused */ $hreflang = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
62
	{
63
		/*$this->setLink(
64
			new RApiHalDocumentLink($href, 'self', $title, $name, $hreflang)
65
		);*/
66
		$this->setData($data);
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type string expected by parameter $rel of RApiHalDocumentResource::setData(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
		$this->setData(/** @scrutinizer ignore-type */ $data);
Loading history...
67
	}
68
69
	/**
70
	 * Gets self link
71
	 *
72
	 * @return RApiHalDocumentLink
73
	 */
74
	public function getSelf()
75
	{
76
		return $this->_links['self'];
77
	}
78
79
	/**
80
	 * Gets all links
81
	 *
82
	 * @return array
83
	 */
84
	public function getLinks()
85
	{
86
		return $this->_links;
87
	}
88
89
	/**
90
	 * Gets all Embeded elements
91
	 *
92
	 * @return array
93
	 */
94
	public function getEmbedded()
95
	{
96
		return $this->_embedded;
97
	}
98
99
	/**
100
	 * Add a link to the resource.
101
	 *
102
	 * Per the JSON-HAL specification, a link relation can reference a
103
	 * single link or an array of links. By default, two or more links with
104
	 * the same relation will be treated as an array of links. The $singular
105
	 * flag will force links with the same relation to be overwritten. The
106
	 * $plural flag will force links with only one relation to be treated
107
	 * as an array of links. The $plural flag has no effect if $singular
108
	 * is set to true.
109
	 *
110
	 * @param   RApiHalDocumentLink  $link      Link
111
	 * @param   bool                 $singular  Force overwrite of the existing link
112
	 * @param   bool                 $plural    Force plural mode even if only one link is present
113
	 *
114
	 * @return RApiHalDocumentResource
115
	 */
116
	public function setLink(RApiHalDocumentLink $link, $singular = false, $plural = false)
117
	{
118
		$rel = $link->getRel();
119
120
		if ($singular || (!isset($this->_links[$rel]) && !$plural))
121
		{
122
			$this->_links[$rel] = $link;
123
		}
124
		else
125
		{
126
			if (isset($this->_links[$rel]) && !is_array($this->_links[$rel]))
127
			{
128
				$orig_link = $this->_links[$rel];
129
				$this->_links[$rel] = array($orig_link);
130
			}
131
132
			$this->_links[$rel][] = $link;
133
		}
134
135
		return $this;
136
	}
137
138
	/**
139
	 * Set multiple links at once
140
	 *
141
	 * @param   array  $links     List of links
142
	 * @param   bool   $singular  Force overwrite of the existing link
143
	 * @param   bool   $plural    Force plural mode even if only one link is present
144
	 *
145
	 * @return RApiHalDocumentResource
146
	 */
147
	public function setLinks(array $links, $singular = false, $plural = false)
148
	{
149
		foreach ($links as $link)
150
		{
151
			$this->setLink($link, $singular, $plural);
152
		}
153
154
		return $this;
155
	}
156
157
	/**
158
	 * Replace existing link to the resource.
159
	 *
160
	 * @param   RApiHalDocumentLink  $link   Link
161
	 * @param   mixed                $group  Groupped link container
162
	 *
163
	 * @return RApiHalDocumentResource
164
	 */
165
	public function setReplacedLink(RApiHalDocumentLink $link, $group = '')
166
	{
167
		$rel = $link->getRel();
168
169
		if ($group !== '')
170
		{
171
			$this->_links[$rel][$group] = $link;
172
		}
173
		else
174
		{
175
			$this->_links[$rel] = $link;
176
		}
177
178
		return $this;
179
	}
180
181
	/**
182
	 * Sets data to the resource
183
	 *
184
	 * @param   string  $rel   Rel element
185
	 * @param   mixed   $data  Data for the resource
186
	 *
187
	 * @return RApiHalDocumentResource
188
	 */
189
	public function setData($rel, $data = null)
190
	{
191
		if (is_array($rel) && null === $data)
0 ignored issues
show
introduced by
The condition is_array($rel) is always false.
Loading history...
192
		{
193
			foreach ($rel as $k => $v)
194
			{
195
				$this->_data[$k] = $v;
196
			}
197
		}
198
		else
199
		{
200
			$this->_data[$rel] = $data;
201
		}
202
203
		return $this;
204
	}
205
206
	/**
207
	 * Sets data to the resource
208
	 *
209
	 * @param   string  $rel       Rel element
210
	 * @param   string  $key       Key value for the resource
211
	 * @param   string  $data      Data value for the resource
212
	 * @param   bool    $singular  Force overwrite of the existing data
213
	 * @param   bool    $plural    Force plural mode even if only one link is present
214
	 *
215
	 * @return RApiHalDocumentResource
216
	 */
217
	public function setDataGrouped($rel, $key = '', $data = '', $singular = false, $plural = false)
218
	{
219
		if ($singular || (!isset($this->_data[$rel]) && !$plural))
220
		{
221
			$this->_data[$rel][$key] = $data;
222
		}
223
		else
224
		{
225
			if (isset($this->_data[$rel]) && !is_array($this->_data[$rel]))
226
			{
227
				$orig_link = $this->_data[$rel];
228
				$this->_data[$rel] = array($orig_link);
229
			}
230
231
			$this->_data[$rel][$key] = $data;
232
		}
233
234
		return $this;
235
	}
236
237
	/**
238
	 * Sets Embedded resource
239
	 *
240
	 * @param   string                   $rel       Relation of the resource
241
	 * @param   RApiHalDocumentResource  $resource  Resource
242
	 * @param   bool                     $singular  Force overwrite of the existing embedded element
243
	 *
244
	 * @return RApiHalDocumentResource
245
	 */
246
	public function setEmbedded($rel, RApiHalDocumentResource $resource = null, $singular = false)
247
	{
248
		if ($singular)
249
		{
250
			$this->_embedded[$rel] = $resource;
251
		}
252
		else
253
		{
254
			$this->_embedded[$rel][] = $resource;
255
		}
256
257
		return $this;
258
	}
259
260
	/**
261
	 * Converts current RApiHalDocumentResource object to Array
262
	 *
263
	 * @return array
264
	 */
265
	public function toArray()
266
	{
267
		$data = array();
268
269
		foreach ($this->_links as $rel => $link)
270
		{
271
			$links = $this->_recourseLinks($link);
272
273
			if (!empty($links))
274
			{
275
				$data['_links'][$rel] = $links;
276
			}
277
		}
278
279
		foreach ($this->_data as $key => $value)
280
		{
281
			$data[$key] = $value;
282
		}
283
284
		foreach ($this->_embedded as $rel => $embed)
285
		{
286
			$data['_embedded'][$rel] = $this->_recourseEmbedded($embed);
287
		}
288
289
		return $data;
290
	}
291
292
	/**
293
	 * Recourse function for Embedded objects
294
	 *
295
	 * @param   RApiHalDocumentResource|null|array  $embedded  Embedded object
296
	 *
297
	 * @return array
298
	 */
299
	protected function _recourseEmbedded($embedded)
300
	{
301
		if (is_null($embedded))
302
		{
303
			return null;
304
		}
305
306
		$result = array();
307
308
		if ($embedded instanceof self)
309
		{
310
			$result = $embedded->toArray();
311
		}
312
		else
313
		{
314
			foreach ($embedded as $embed)
315
			{
316
				if ($embed instanceof self)
317
				{
318
					$result[] = $embed->toArray();
319
				}
320
			}
321
		}
322
323
		return $result;
324
	}
325
326
	/**
327
	 * Recourse function for Link objects
328
	 *
329
	 * @param   array|RApiHalDocumentLink  $links  Link object
330
	 *
331
	 * @return array
332
	 */
333
	protected function _recourseLinks($links)
334
	{
335
		$result = array();
336
337
		if (!is_array($links))
338
		{
339
			$result = $links->toArray();
340
		}
341
		else
342
		{
343
			foreach ($links as $link)
344
			{
345
				$result[] = $link->toArray();
346
			}
347
		}
348
349
		return $result;
350
	}
351
352
	/**
353
	 * Convert function to Json format
354
	 *
355
	 * @return string
356
	 */
357
	public function toJson()
358
	{
359
		// Check for defined constants
360
		if (!defined('JSON_UNESCAPED_SLASHES'))
361
		{
362
			define('JSON_UNESCAPED_SLASHES', 64);
363
		}
364
365
		if (defined(JSON_NUMERIC_CHECK) && $this->jsonNumericCheck)
366
		{
367
			return json_encode($this->toArray(), JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
368
		}
369
370
		return json_encode($this->toArray(), JSON_UNESCAPED_SLASHES);
371
	}
372
373
	/**
374
	 * Convert function to string format
375
	 *
376
	 * @return string
377
	 */
378
	public function __toString()
379
	{
380
		return $this->toJson();
381
	}
382
383
	/**
384
	 * Get XML document of current resource
385
	 *
386
	 * @param   SimpleXMLElement|null  $xml  XML document
387
	 *
388
	 * @return SimpleXMLElement
389
	 */
390
	public function getXML($xml = null)
391
	{
392
		if (!($xml instanceof SimpleXMLElement))
393
		{
394
			$xml = new SimpleXMLElement('<resource></resource>');
395
		}
396
397
		$this->xml = $xml;
398
399
		foreach ($this->_links as $links)
400
		{
401
			if (is_array($links))
402
			{
403
				foreach ($links as $link)
404
				{
405
					$this->_addLinks($link);
406
				}
407
			}
408
			else
409
			{
410
				$this->_addLinks($links);
411
			}
412
		}
413
414
		$this->_addData($this->xml, $this->_data);
415
		$this->_getEmbedded($this->_embedded);
416
417
		return $this->xml;
418
	}
419
420
	/**
421
	 * Get Embedded of current resource
422
	 *
423
	 * @param   mixed        $embedded  Embedded list
424
	 * @param   string|null  $_rel      Relation of embedded object
425
	 *
426
	 * @return void
427
	 */
428
	protected function _getEmbedded($embedded, $_rel = null)
429
	{
430
		/* @var $embed RApiHalDocumentResource */
431
		foreach ($embedded as $rel => $embed)
432
		{
433
			if ($embed instanceof RApiHalDocumentResource)
434
			{
435
				$rel = is_numeric($rel) ? $_rel : $rel;
436
				$this->_getEmbRes($embed)->addAttribute('rel', $rel);
437
			}
438
			else
439
			{
440
				if (!is_null($embed))
441
				{
442
					$this->_getEmbedded($embed, $rel);
443
				}
444
				else
445
				{
446
					$rel = is_numeric($rel) ? $_rel : $rel;
447
					$this->xml->addChild('resource')->addAttribute('rel', $rel);
448
				}
449
			}
450
		}
451
	}
452
453
	/**
454
	 * Get Embedded of current resource in XML format
455
	 *
456
	 * @param   RApiHalDocumentResource  $embed  Embedded object
457
	 *
458
	 * @return SimpleXMLElement
459
	 */
460
	protected function _getEmbRes(RApiHalDocumentResource $embed)
461
	{
462
		$resource = $this->xml->addChild('resource');
463
464
		return $embed->getXML($resource);
465
	}
466
467
	/**
468
	 * Sets XML document to this resource
469
	 *
470
	 * @param   SimpleXMLElement  $xml  XML document
471
	 *
472
	 * @return RApiHalDocumentResource
473
	 */
474
	public function setXML(SimpleXMLElement $xml)
475
	{
476
		$this->xml = $xml;
477
478
		return $this;
479
	}
480
481
	/**
482
	 * Adds data to the current resource
483
	 *
484
	 * @param   SimpleXMLElement  $xml          XML document
485
	 * @param   array             $data         Data
486
	 * @param   string            $keyOverride  Key override
487
	 *
488
	 * @return void
489
	 */
490
	protected function _addData(SimpleXMLElement $xml, array $data, $keyOverride = null)
491
	{
492
		foreach ($data as $key => $value)
493
		{
494
			// Alpha-numeric key => array value
495
			if (!is_numeric($key) && is_array($value))
496
			{
497
				$c = $xml->addChild($key);
498
				$this->_addData($c, $value, $key);
499
			}
500
			// Alpha-numeric key => non-array value
501
			elseif (!is_numeric($key) && !is_array($value))
502
			{
503
				$xml->addChild($key, $value);
504
			}
505
			// Numeric key => array value
506
			elseif (is_array($value))
507
			{
508
				$this->_addData($xml, $value);
509
			}
510
			// Numeric key => non-array value
511
			else
512
			{
513
				$xml->addChild($keyOverride, $value);
514
			}
515
		}
516
	}
517
518
	/**
519
	 * Adds links to the current resource
520
	 *
521
	 * @param   RApiHalDocumentLink  $link  Link
522
	 *
523
	 * @return void
524
	 */
525
	protected function _addLinks(RApiHalDocumentLink $link)
526
	{
527
		if ($link->getRel() != 'self' && !is_numeric($link->getRel()))
528
		{
529
			$this->_addLink($link);
530
		}
531
	}
532
533
	/**
534
	 * Adds link to the current resource
535
	 *
536
	 * @param   RApiHalDocumentLink  $link  Link
537
	 *
538
	 * @return RApiHalDocumentResource
539
	 */
540
	protected function _addLink(RApiHalDocumentLink $link)
541
	{
542
		$this->setXMLAttributes($this->xml->addChild('link'), $link);
543
544
		return $this;
545
	}
546
547
	/**
548
	 * Method to load an object or an array into this HAL object.
549
	 *
550
	 * @param   object  $object  Object whose properties are to be loaded.
551
	 *
552
	 * @return object This method may be chained.
553
	 */
554
	public function load($object)
555
	{
556
		foreach ($object as $name => $value)
557
		{
558
			// For _links and _embedded, we merge rather than replace.
559
			if ($name == '_links')
560
			{
561
				$this->_links = array_merge((array) $this->_links, (array) $value);
562
			}
563
			elseif ($name == '_embedded')
564
			{
565
				$this->_embedded = array_merge((array) $this->_embedded, (array) $value);
566
			}
567
			else
568
			{
569
				$this->_data[$name] = $value;
570
			}
571
		}
572
573
		return $this;
574
	}
575
576
	/**
577
	 * Sets the ability to perform numeric to int conversion of the JSON output.
578
	 *
579
	 * <b>Example Usage:</b>
580
	 * <code>
581
	 * $hal->setJsonNumericCheck($jsonNumericCheck = self::JSON_NUMERIC_CHECK_OFF);
582
	 * $hal->setJsonNumericCheck($jsonNumericCheck = self::JSON_NUMERIC_CHECK_ON);
583
	 * </code>
584
	 *
585
	 * @param   bool  $jsonNumericCheck  Json numeric check
586
	 *
587
	 * @return RApiHalDocumentResource
588
	 */
589
	public function setJsonNumericCheck($jsonNumericCheck = self::JSON_NUMERIC_CHECK_OFF)
590
	{
591
		$this->jsonNumericCheck = $jsonNumericCheck;
592
593
		return $this;
594
	}
595
596
	/**
597
	 * Creates empty array of Xml configuration Resource field
598
	 *
599
	 * @param   array   $resource          Resource array
600
	 * @param   string  $resourceSpecific  Resource specific container
601
	 *
602
	 * @return array
603
	 */
604
	public static function defaultResourceField($resource = array(), $resourceSpecific = 'rcwsGlobal')
605
	{
606
		$defaultResource = array(
607
			'resourceSpecific' => !empty($resource['resourceSpecific']) ? $resource['resourceSpecific'] : $resourceSpecific,
608
			'displayGroup'     => !empty($resource['displayGroup']) ? $resource['displayGroup'] : '',
609
			'displayName'      => !empty($resource['displayName']) ? $resource['displayName'] : '',
610
			'fieldFormat'      => !empty($resource['fieldFormat']) ? $resource['fieldFormat'] : '',
611
			'transform'        => !empty($resource['transform']) ? $resource['transform'] : '',
612
			'linkName'         => !empty($resource['linkName']) ? $resource['linkName'] : '',
613
			'linkTitle'        => !empty($resource['linkTitle']) ? $resource['linkTitle'] : '',
614
			'hrefLang'         => !empty($resource['hrefLang']) ? $resource['hrefLang'] : '',
615
			'linkTemplated'    => !empty($resource['linkTemplated']) ? $resource['linkTemplated'] : '',
616
			'linkRel'          => !empty($resource['linkRel']) ? $resource['linkRel'] : '',
617
			'description'      => !empty($resource['description']) ? $resource['description'] : '',
618
		);
619
620
		return array_merge($resource, $defaultResource);
621
	}
622
623
	/**
624
	 * Merges two resource fields
625
	 *
626
	 * @param   array  $resourceMain   Resource array main
627
	 * @param   array  $resourceChild  Resource array child
628
	 *
629
	 * @return array
630
	 */
631
	public static function mergeResourceFields($resourceMain = array(), $resourceChild = array())
632
	{
633
		foreach ($resourceMain as $key => $value)
634
		{
635
			$resourceMain[$key] = !empty($resourceChild[$key]) ? $resourceChild[$key] : $resourceMain[$key];
636
		}
637
638
		return $resourceMain;
639
	}
640
}
641