Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
microdata.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage Microdata
5  *
6  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * Joomla Platform class for interacting with Microdata semantics.
14  *
15  * @package Joomla.Platform
16  * @subpackage Microdata
17  * @since 3.2
18  */
20 {
21  /**
22  * Array with all available Types and Properties
23  *
24  * @var array
25  * @since 3.2
26  */
27  protected static $types = null;
28 
29  /**
30  * The Schema.org Type
31  *
32  * @var string
33  * @since 3.2
34  */
35  protected $type = null;
36 
37  /**
38  * The Property
39  *
40  * @var string
41  * @since 3.2
42  */
43  protected $property = null;
44 
45  /**
46  * The Human value or Machine value
47  *
48  * @var string
49  * @since 3.2
50  */
51  protected $content = null;
52 
53  /**
54  * The Machine value
55  *
56  * @var string
57  * @since 3.2
58  */
59  protected $machineContent = null;
60 
61  /**
62  * Fallback Type
63  *
64  * @var string
65  * @since 3.2
66  */
67  protected $fallbackType = null;
68 
69  /**
70  * Fallback Property
71  *
72  * @var string
73  * @since 3.2
74  */
75  protected $fallbackProperty = null;
76 
77  /**
78  * Used to check if a Fallback must be used
79  *
80  * @var string
81  * @since 3.2
82  */
83  protected $fallback = false;
84 
85  /**
86  * Used to check if the Microdata semantics output are enabled or disabled
87  *
88  * @var boolean
89  * @since 3.2
90  */
91  protected $enabled = true;
92 
93  /**
94  * Initialize the class and setup the default Type
95  *
96  * @param string $type Optional, Fallback to Thing Type
97  * @param boolean $flag Enable or disable microdata output
98  *
99  * @since 3.2
100  */
101  public function __construct($type = '', $flag = true)
102  {
103  if ($this->enabled = (boolean) $flag)
104  {
105  // Fallback to Thing Type
106  if (!$type)
107  {
108  $type = 'Thing';
109  }
110 
111  $this->setType($type);
112  }
113  }
114 
115  /**
116  * Load all Types and Properties from the types.json file
117  *
118  * @return void
119  *
120  * @since 3.2
121  */
122  protected static function loadTypes()
123  {
124  // Load the JSON
125  if (!static::$types)
126  {
127  $path = JPATH_PLATFORM . '/joomla/microdata/types.json';
128  static::$types = json_decode(file_get_contents($path), true);
129  }
130  }
131 
132  /**
133  * Enable or Disable Microdata semantics output
134  *
135  * @param boolean $flag Enable or disable microdata output
136  *
137  * @return JMicrodata Instance of $this
138  *
139  * @since 3.2
140  */
141  public function enable($flag = true)
142  {
143  $this->enabled = (boolean) $flag;
144 
145  return $this;
146  }
147 
148  /**
149  * Return true if Microdata semantics output are enabled
150  *
151  * @return boolean
152  *
153  * @since 3.2
154  */
155  public function isEnabled()
156  {
157  return ($this->enabled) ? true : false;
158  }
159 
160  /**
161  * Set a new Schema.org Type
162  *
163  * @param string $type The Type to be setup
164  *
165  * @return JMicrodata Instance of $this
166  *
167  * @since 3.2
168  */
169  public function setType($type)
170  {
171  if (!$this->enabled)
172  {
173  return $this;
174  }
175 
176  // Sanitize the Type
177  $this->type = static::sanitizeType($type);
178 
179  // If the given Type isn't available, fallback to Thing
180  if (!static::isTypeAvailable($this->type))
181  {
182  $this->type = 'Thing';
183  }
184 
185  return $this;
186  }
187 
188  /**
189  * Return the current Type name
190  *
191  * @return string
192  *
193  * @since 3.2
194  */
195  public function getType()
196  {
197  return $this->type;
198  }
199 
200  /**
201  * Setup a Property
202  *
203  * @param string $property The Property
204  *
205  * @return JMicrodata Instance of $this
206  *
207  * @since 3.2
208  */
209  public function property($property)
210  {
211  if (!$this->enabled)
212  {
213  return $this;
214  }
215 
216  // Sanitize the Property
217  $property = static::sanitizeProperty($property);
218 
219  // Control if the Property exist in the given Type and setup it, if not leave NULL
220  if (static::isPropertyInType($this->type, $property))
221  {
222  $this->property = $property;
223  }
224  else
225  {
226  $this->fallback = true;
227  }
228 
229  return $this;
230  }
231 
232  /**
233  * Return the property variable
234  *
235  * @return string
236  *
237  * @since 3.2
238  */
239  public function getProperty()
240  {
241  return $this->property;
242  }
243 
244  /**
245  * Setup a Text value or Content value for the Microdata
246  *
247  * @param string $value The human value or marchine value to be used
248  * @param string $machineValue The machine value
249  *
250  * @return JMicrodata Instance of $this
251  *
252  * @since 3.2
253  */
254  public function content($value, $machineValue = null)
255  {
256  $this->content = $value;
257  $this->machineContent = $machineValue;
258 
259  return $this;
260  }
261 
262  /**
263  * Return the content variable
264  *
265  * @return string
266  *
267  * @since 3.2
268  */
269  public function getContent()
270  {
271  return $this->content;
272  }
273 
274  /**
275  * Setup a Fallback Type and Property
276  *
277  * @param string $type The Fallback Type
278  * @param string $property The Fallback Property
279  *
280  * @return JMicrodata Instance of $this
281  *
282  * @since 3.2
283  */
284  public function fallback($type, $property)
285  {
286  if (!$this->enabled)
287  {
288  return $this;
289  }
290 
291  // Sanitize the Type
292  $this->fallbackType = static::sanitizeType($type);
293 
294  // If the given Type isn't available, fallback to Thing
295  if (!static::isTypeAvailable($this->fallbackType))
296  {
297  $this->fallbackType = 'Thing';
298  }
299 
300  // Control if the Property exist in the given Type and setup it, if not leave NULL
301  if (static::isPropertyInType($this->fallbackType, $property))
302  {
303  $this->fallbackProperty = $property;
304  }
305  else
306  {
307  $this->fallbackProperty = null;
308  }
309 
310  return $this;
311  }
312 
313  /**
314  * Return the fallbackType variable
315  *
316  * @return string
317  *
318  * @since 3.2
319  */
320  public function getFallbackType()
321  {
322  return $this->fallbackType;
323  }
324 
325  /**
326  * Return the fallbackProperty variable
327  *
328  * @return string
329  *
330  * @since 3.2
331  */
332  public function getFallbackProperty()
333  {
334  return $this->fallbackProperty;
335  }
336 
337  /**
338  * This function handle the logic of a Microdata intelligent display.
339  * Check if the Type, Property are available, if not check for a Fallback,
340  * then reset all params for the next use and
341  * return the Microdata HTML
342  *
343  * @param string $displayType Optional, 'inline', available ['inline'|'span'|'div'|meta]
344  * @param boolean $emptyOutput Return an empty string if the microdata output is disabled and there is a $content value
345  *
346  * @return string
347  *
348  * @since 3.2
349  */
350  public function display($displayType = '', $emptyOutput = false)
351  {
352  // Initialize the HTML to output
353  $html = ($this->content !== null) ? $this->content : '';
354 
355  // Control if the Microdata output is enabled, otherwise return the content or empty string
356  if (!$this->enabled)
357  {
358  return ($emptyOutput) ? '' : $html;
359  }
360 
361  // If the property is wrong for the current Type check if Fallback available, otherwise return empty HTML
362  if ($this->property && !$this->fallback)
363  {
364  // Process and return the HTML the way the user expects to
365  if ($displayType)
366  {
367  switch ($displayType)
368  {
369  case 'span':
370  $html = static::htmlSpan($html, $this->property);
371  break;
372 
373  case 'div':
374  $html = static::htmlDiv($html, $this->property);
375  break;
376 
377  case 'meta':
378  $html = ($this->machineContent !== null) ? $this->machineContent : $html;
379  $html = static::htmlMeta($html, $this->property);
380  break;
381 
382  default:
383  // Default $displayType = 'inline'
384  $html = static::htmlProperty($this->property);
385  break;
386  }
387  }
388  else
389  {
390  /*
391  * Process and return the HTML in an automatic way,
392  * with the Property expected Types and display the Microdata in the right way,
393  * check if the Property is normal, nested or must be rendered in a metadata tag
394  */
395  switch (static::getExpectedDisplayType($this->type, $this->property))
396  {
397  case 'nested':
398  // Retrive the expected nested Type of the Property
399  $nestedType = static::getExpectedTypes($this->type, $this->property);
400  $nestedProperty = '';
401 
402  // If there is a Fallback Type then probably it could be the expectedType
403  if (in_array($this->fallbackType, $nestedType))
404  {
405  $nestedType = $this->fallbackType;
406 
407  if ($this->fallbackProperty)
408  {
409  $nestedProperty = $this->fallbackProperty;
410  }
411  }
412  else
413  {
414  $nestedType = $nestedType[0];
415  }
416 
417  // Check if a Content is available, otherwise Fallback to an 'inline' display type
418  if ($this->content !== null)
419  {
420  if ($nestedProperty)
421  {
422  $html = static::htmlSpan(
423  $this->content,
424  $nestedProperty
425  );
426  }
427 
428  $html = static::htmlSpan(
429  $html,
430  $this->property,
431  $nestedType,
432  true
433  );
434  }
435  else
436  {
437  $html = static::htmlProperty($this->property) . ' ' . static::htmlScope($nestedType);
438 
439  if ($nestedProperty)
440  {
441  $html .= ' ' . static::htmlProperty($nestedProperty);
442  }
443  }
444 
445  break;
446 
447  case 'meta':
448  // Check if the Content value is available, otherwise Fallback to an 'inline' display Type
449  if ($this->content !== null)
450  {
451  $html = ($this->machineContent !== null) ? $this->machineContent : $this->content;
452  $html = static::htmlMeta($html, $this->property) . $this->content;
453  }
454  else
455  {
456  $html = static::htmlProperty($this->property);
457  }
458 
459  break;
460 
461  default:
462  /*
463  * Default expected display type = 'normal'
464  * Check if the Content value is available,
465  * otherwise Fallback to an 'inline' display Type
466  */
467  if ($this->content !== null)
468  {
469  $html = static::htmlSpan($this->content, $this->property);
470  }
471  else
472  {
473  $html = static::htmlProperty($this->property);
474  }
475 
476  break;
477  }
478  }
479  }
480  elseif ($this->fallbackProperty)
481  {
482  // Process and return the HTML the way the user expects to
483  if ($displayType)
484  {
485  switch ($displayType)
486  {
487  case 'span':
488  $html = static::htmlSpan($html, $this->fallbackProperty, $this->fallbackType);
489  break;
490 
491  case 'div':
492  $html = static::htmlDiv($html, $this->fallbackProperty, $this->fallbackType);
493  break;
494 
495  case 'meta':
496  $html = ($this->machineContent !== null) ? $this->machineContent : $html;
497  $html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
498  break;
499 
500  default:
501  // Default $displayType = 'inline'
502  $html = static::htmlScope($type::scope()) . ' ' . static::htmlProperty($this->fallbackProperty);
503  break;
504  }
505  }
506  else
507  {
508  /*
509  * Process and return the HTML in an automatic way,
510  * with the Property expected Types an display the Microdata in the right way,
511  * check if the Property is nested or must be rendered in a metadata tag
512  */
513  switch (static::getExpectedDisplayType($this->fallbackType, $this->fallbackProperty))
514  {
515  case 'meta':
516  // Check if the Content value is available, otherwise Fallback to an 'inline' display Type
517  if ($this->content !== null)
518  {
519  $html = ($this->machineContent !== null) ? $this->machineContent : $this->content;
520  $html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
521  }
522  else
523  {
524  $html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
525  }
526 
527  break;
528 
529  default:
530  /*
531  * Default expected display type = 'normal'
532  * Check if the Content value is available,
533  * otherwise Fallback to an 'inline' display Type
534  */
535  if ($this->content !== null)
536  {
537  $html = static::htmlSpan($this->content, $this->fallbackProperty);
538  $html = static::htmlSpan($html, '', $this->fallbackType);
539  }
540  else
541  {
542  $html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
543  }
544 
545  break;
546  }
547  }
548  }
549  elseif (!$this->fallbackProperty && $this->fallbackType !== null)
550  {
551  $html = static::htmlScope($this->fallbackType);
552  }
553 
554  // Reset params
555  $this->content = null;
556  $this->property = null;
557  $this->fallbackProperty = null;
558  $this->fallbackType = null;
559  $this->fallback = false;
560 
561  return $html;
562  }
563 
564  /**
565  * Return the HTML of the current Scope
566  *
567  * @return string
568  *
569  * @since 3.2
570  */
571  public function displayScope()
572  {
573  // Control if the Microdata output is enabled, otherwise return the content or empty string
574  if (!$this->enabled)
575  {
576  return '';
577  }
578 
579  return static::htmlScope($this->type);
580  }
581 
582  /**
583  * Return the sanitized Type
584  *
585  * @param string $type The Type to sanitize
586  *
587  * @return string
588  *
589  * @since 3.2
590  */
591  public static function sanitizeType($type)
592  {
593  return ucfirst(trim($type));
594  }
595 
596  /**
597  * Return the sanitized Property
598  *
599  * @param string $property The Property to sanitize
600  *
601  * @return string
602  *
603  * @since 3.2
604  */
605  public static function sanitizeProperty($property)
606  {
607  return lcfirst(trim($property));
608  }
609 
610  /**
611  * Return an array with all Types and Properties
612  *
613  * @return array
614  *
615  * @since 3.2
616  */
617  public static function getTypes()
618  {
619  static::loadTypes();
620 
621  return static::$types;
622  }
623 
624  /**
625  * Return an array with all available Types
626  *
627  * @return array
628  *
629  * @since 3.2
630  */
631  public static function getAvailableTypes()
632  {
633  static::loadTypes();
634 
635  return array_keys(static::$types);
636  }
637 
638  /**
639  * Return the expected types of the Property
640  *
641  * @param string $type The Type to process
642  * @param string $property The Property to process
643  *
644  * @return array
645  *
646  * @since 3.2
647  */
648  public static function getExpectedTypes($type, $property)
649  {
650  static::loadTypes();
651 
652  $tmp = static::$types[$type]['properties'];
653 
654  // Check if the Property is in the Type
655  if (isset($tmp[$property]))
656  {
657  return $tmp[$property]['expectedTypes'];
658  }
659 
660  // Check if the Property is inherit
661  $extendedType = static::$types[$type]['extends'];
662 
663  // Recursive
664  if (!empty($extendedType))
665  {
666  return static::getExpectedTypes($extendedType, $property);
667  }
668 
669  return array();
670  }
671 
672  /**
673  * Return the expected display type of the [normal|nested|meta]
674  * In wich way to display the Property:
675  * normal -> itemprop="name"
676  * nested -> itemprop="director" itemscope itemtype="http://schema.org/Person"
677  * meta -> <meta itemprop="datePublished" content="1991-05-01">
678  *
679  * @param string $type The Type where to find the Property
680  * @param string $property The Property to process
681  *
682  * @return string
683  *
684  * @since 3.2
685  */
686  protected static function getExpectedDisplayType($type, $property)
687  {
688  $expectedTypes = static::getExpectedTypes($type, $property);
689 
690  // Retrieve the first expected type
691  $type = $expectedTypes[0];
692 
693  // Check if it's a meta display
694  if ($type === 'Date' || $type === 'DateTime' || $property === 'interactionCount')
695  {
696  return 'meta';
697  }
698 
699  // Check if it's a normal display
700  if ($type === 'Text' || $type === 'URL' || $type === 'Boolean' || $type === 'Number')
701  {
702  return 'normal';
703  }
704 
705  // Otherwise it's a nested display
706  return 'nested';
707  }
708 
709  /**
710  * Recursive function, control if the given Type has the given Property
711  *
712  * @param string $type The Type where to check
713  * @param string $property The Property to check
714  *
715  * @return boolean
716  *
717  * @since 3.2
718  */
719  public static function isPropertyInType($type, $property)
720  {
721  if (!static::isTypeAvailable($type))
722  {
723  return false;
724  }
725 
726  // Control if the Property exists, and return true
727  if (array_key_exists($property, static::$types[$type]['properties']))
728  {
729  return true;
730  }
731 
732  // Recursive: Check if the Property is inherit
733  $extendedType = static::$types[$type]['extends'];
734 
735  if (!empty($extendedType))
736  {
737  return static::isPropertyInType($extendedType, $property);
738  }
739 
740  return false;
741  }
742 
743  /**
744  * Control if the given Type class is available
745  *
746  * @param string $type The Type to check
747  *
748  * @return boolean
749  *
750  * @since 3.2
751  */
752  public static function isTypeAvailable($type)
753  {
754  static::loadTypes();
755 
756  return (array_key_exists($type, static::$types)) ? true : false;
757  }
758 
759  /**
760  * Return the microdata in a <meta> tag with the machine content inside.
761  *
762  * @param string $content The machine content to display
763  * @param string $property The Property
764  * @param string $scope Optional, the Type scope to display
765  * @param boolean $inverse Optional, default = false, inverse the $scope with the $property
766  *
767  * @return string
768  *
769  * @since 3.2
770  */
771  public static function htmlMeta($content, $property, $scope = '', $inverse = false)
772  {
773  // Control if the Property has allready the itemprop
774  if (stripos($property, 'itemprop') !== 0)
775  {
776  $property = static::htmlProperty($property);
777  }
778 
779  // Control if the Scope have allready the itemtype
780  if (!empty($scope) && stripos($scope, 'itemscope') !== 0)
781  {
782  $scope = static::htmlScope($scope);
783  }
784 
785  if ($inverse)
786  {
787  $tmp = join(' ', array($property, $scope));
788  }
789  else
790  {
791  $tmp = join(' ', array($scope, $property));
792  }
793 
794  $tmp = trim($tmp);
795 
796  return "<meta $tmp content='$content'/>";
797  }
798 
799  /**
800  * Return the microdata in an <span> tag.
801  *
802  * @param string $content The human value
803  * @param string $property Optional, the human value to display
804  * @param string $scope Optional, the Type scope to display
805  * @param boolean $inverse Optional, default = false, inverse the $scope with the $property
806  *
807  * @return string
808  *
809  * @since 3.2
810  */
811  public static function htmlSpan($content, $property = '', $scope = '', $inverse = false)
812  {
813  // Control if the Property has allready the itemprop
814  if (!empty($property) && stripos($property, 'itemprop') !== 0)
815  {
816  $property = static::htmlProperty($property);
817  }
818 
819  // Control if the Scope have allready the itemtype
820  if (!empty($scope) && stripos($scope, 'itemscope') !== 0)
821  {
822  $scope = static::htmlScope($scope);
823  }
824 
825  if ($inverse)
826  {
827  $tmp = join(' ', array($property, $scope));
828  }
829  else
830  {
831  $tmp = join(' ', array($scope, $property));
832  }
833 
834  $tmp = trim($tmp);
835  $tmp = ($tmp) ? ' ' . $tmp : '';
836 
837  return "<span$tmp>$content</span>";
838  }
839 
840  /**
841  * Return the microdata in an <div> tag.
842  *
843  * @param string $content The human value
844  * @param string $property Optional, the human value to display
845  * @param string $scope Optional, the Type scope to display
846  * @param boolean $inverse Optional, default = false, inverse the $scope with the $property
847  *
848  * @return string
849  *
850  * @since 3.2
851  */
852  public static function htmlDiv($content, $property = '', $scope = '', $inverse = false)
853  {
854  // Control if the Property has allready the itemprop
855  if (!empty($property) && stripos($property, 'itemprop') !== 0)
856  {
857  $property = static::htmlProperty($property);
858  }
859 
860  // Control if the Scope have allready the itemtype
861  if (!empty($scope) && stripos($scope, 'itemscope') !== 0)
862  {
863  $scope = static::htmlScope($scope);
864  }
865 
866  if ($inverse)
867  {
868  $tmp = join(' ', array($property, $scope));
869  }
870  else
871  {
872  $tmp = join(' ', array($scope, $property));
873  }
874 
875  $tmp = trim($tmp);
876  $tmp = ($tmp) ? ' ' . $tmp : '';
877 
878  return "<div$tmp>$content</div>";
879  }
880 
881  /**
882  * Return the HTML Scope
883  *
884  * @param string $scope The Scope to process
885  *
886  * @return string
887  *
888  * @since 3.2
889  */
890  public static function htmlScope($scope)
891  {
892  if (stripos($scope, 'http') !== 0)
893  {
894  $scope = 'https://schema.org/' . ucfirst($scope);
895  }
896 
897  return "itemscope itemtype='$scope'";
898  }
899 
900  /**
901  * Return the HTML Property
902  *
903  * @param string $property The Property to process
904  *
905  * @return string
906  *
907  * @since 3.2
908  */
909  public static function htmlProperty($property)
910  {
911  return "itemprop='$property'";
912  }
913 }