- md5
- 73400789f4a2ff36e4ebd3d24377a064
- sha1
- 87b66bbc8f59cb0607f7fd87e182d433ecbc3d0f
- sha256
- 037422aa5d68837f26c1338f424476c911e1d4f60930f36c37ddc17bb1991e56
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Microdata;
// phpcs:disable PSR1.Files.SideEffects
\defined('JPATH_PLATFORM') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla Platform class for interacting with Microdata semantics.
*
* @since 3.2
*/
class Microdata
{
/**
* Array with all available Types and Properties from the http://schema.org vocabulary
*
* @var array
* @since 3.2
*/
protected static $types = null;
/**
* The Type
*
* @var string
* @since 3.2
*/
protected $type = null;
/**
* The Property
*
* @var string
* @since 3.2
*/
protected $property = null;
/**
* The Human content
*
* @var string
* @since 3.2
*/
protected $content = null;
/**
* The Machine content
*
* @var string
* @since 3.2
*/
protected $machineContent = null;
/**
* The Fallback Type
*
* @var string
* @since 3.2
*/
protected $fallbackType = null;
/**
* The Fallback Property
*
* @var string
* @since 3.2
*/
protected $fallbackProperty = null;
/**
* Used for checking if the library output is enabled or disabled
*
* @var boolean
* @since 3.2
*/
protected $enabled = true;
/**
* Initialize the class and setup the default $Type
*
* @param string $type Optional, fallback to 'Thing' Type
* @param boolean $flag Enable or disable the library output
*
* @since 3.2
*/
public function __construct($type = '', $flag = true)
{
if ($this->enabled = (bool) $flag) {
// Fallback to 'Thing' Type
if (!$type) {
$type = 'Thing';
}
$this->setType($type);
}
}
/**
* Load all available Types and Properties from the http://schema.org vocabulary contained in the types.json file
*
* @return void
*
* @since 3.2
*/
protected static function loadTypes()
{
// Load the JSON
if (!static::$types) {
$path = __DIR__ . '/types.json';
static::$types = json_decode(file_get_contents($path), true);
}
}
/**
* Reset all params
*
* @return void
*
* @since 3.2
*/
protected function resetParams()
{
$this->content = null;
$this->machineContent = null;
$this->property = null;
$this->fallbackProperty = null;
$this->fallbackType = null;
}
/**
* Enable or Disable the library output
*
* @param boolean $flag Enable or disable the library output
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function enable($flag = true)
{
$this->enabled = (bool) $flag;
return $this;
}
/**
* Return 'true' if the library output is enabled
*
* @return boolean
*
* @since 3.2
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Set a new http://schema.org Type
*
* @param string $type The $Type to be setup
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function setType($type)
{
if (!$this->enabled) {
return $this;
}
// Sanitize the Type
$this->type = static::sanitizeType($type);
// If the given $Type isn't available, fallback to 'Thing' Type
if (!static::isTypeAvailable($this->type)) {
$this->type = 'Thing';
}
return $this;
}
/**
* Return the current $Type name
*
* @return string
*
* @since 3.2
*/
public function getType()
{
return $this->type;
}
/**
* Setup a $Property
*
* @param string $property The Property
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function property($property)
{
if (!$this->enabled) {
return $this;
}
// Sanitize the $Property
$property = static::sanitizeProperty($property);
// Control if the $Property exists in the given $Type and setup it, otherwise leave it 'NULL'
if (static::isPropertyInType($this->type, $property)) {
$this->property = $property;
}
return $this;
}
/**
* Return the current $Property name
*
* @return string
*
* @since 3.2
*/
public function getProperty()
{
return $this->property;
}
/**
* Setup a Human content or content for the Machines
*
* @param string $content The human content or machine content to be used
* @param string $machineContent The machine content
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function content($content, $machineContent = null)
{
$this->content = $content;
$this->machineContent = $machineContent;
return $this;
}
/**
* Return the current $content
*
* @return string
*
* @since 3.2
*/
public function getContent()
{
return $this->content;
}
/**
* Return the current $machineContent
*
* @return string
*
* @since 3.3
*/
public function getMachineContent()
{
return $this->machineContent;
}
/**
* Setup a Fallback Type and Property
*
* @param string $type The Fallback Type
* @param string $property The Fallback Property
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function fallback($type, $property)
{
if (!$this->enabled) {
return $this;
}
// Sanitize the $Type
$this->fallbackType = static::sanitizeType($type);
// If the given $Type isn't available, fallback to 'Thing' Type
if (!static::isTypeAvailable($this->fallbackType)) {
$this->fallbackType = 'Thing';
}
// Control if the $Property exist in the given $Type and setup it, otherwise leave it 'NULL'
if (static::isPropertyInType($this->fallbackType, $property)) {
$this->fallbackProperty = $property;
} else {
$this->fallbackProperty = null;
}
return $this;
}
/**
* Return the current $fallbackType
*
* @return string
*
* @since 3.2
*/
public function getFallbackType()
{
return $this->fallbackType;
}
/**
* Return the current $fallbackProperty
*
* @return string
*
* @since 3.2
*/
public function getFallbackProperty()
{
return $this->fallbackProperty;
}
/**
* This function handles the display logic.
* It checks if the Type, Property are available, if not check for a Fallback,
* then reset all params for the next use and return the HTML.
*
* @param string $displayType Optional, 'inline', available options ['inline'|'span'|'div'|meta]
* @param boolean $emptyOutput Return an empty string if the library output is disabled and there is a $content value
*
* @return string
*
* @since 3.2
*/
public function display($displayType = '', $emptyOutput = false)
{
// Initialize the HTML to output
$html = ($this->content !== null && !$emptyOutput) ? $this->content : '';
// Control if the library output is enabled, otherwise return the $content or an empty string
if (!$this->enabled) {
// Reset params
$this->resetParams();
return $html;
}
// If the $property is wrong for the current $Type check if a Fallback is available, otherwise return an empty HTML
if ($this->property) {
// Process and return the HTML the way the user expects to
if ($displayType) {
switch ($displayType) {
case 'span':
$html = static::htmlSpan($html, $this->property);
break;
case 'div':
$html = static::htmlDiv($html, $this->property);
break;
case 'meta':
$html = $this->machineContent ?? $html;
$html = static::htmlMeta($html, $this->property);
break;
default:
// Default $displayType = 'inline'
$html = static::htmlProperty($this->property);
break;
}
} else {
/*
* Process and return the HTML in an automatic way,
* with the $Property expected Types and display everything in the right way,
* check if the $Property is 'normal', 'nested' or must be rendered in a metadata tag
*/
switch (static::getExpectedDisplayType($this->type, $this->property)) {
case 'nested':
// Retrieve the expected 'nested' Type of the $Property
$nestedType = static::getExpectedTypes($this->type, $this->property);
$nestedProperty = '';
// If there is a Fallback Type then probably it could be the expectedType
if (\in_array($this->fallbackType, $nestedType)) {
$nestedType = $this->fallbackType;
if ($this->fallbackProperty) {
$nestedProperty = $this->fallbackProperty;
}
} else {
$nestedType = $nestedType[0];
}
// Check if a $content is available, otherwise fallback to an 'inline' display type
if ($this->content !== null) {
if ($nestedProperty) {
$html = static::htmlSpan(
$this->content,
$nestedProperty
);
}
$html = static::htmlSpan(
$html,
$this->property,
$nestedType,
true
);
} else {
$html = static::htmlProperty($this->property) . ' ' . static::htmlScope($nestedType);
if ($nestedProperty) {
$html .= ' ' . static::htmlProperty($nestedProperty);
}
}
break;
case 'meta':
// Check if a $content is available, otherwise fallback to an 'inline' display type
if ($this->content !== null) {
$html = $this->machineContent ?? $this->content;
$html = static::htmlMeta($html, $this->property) . $this->content;
} else {
$html = static::htmlProperty($this->property);
}
break;
default:
/*
* Default expected display type = 'normal'
* Check if a $content is available,
* otherwise fallback to an 'inline' display type
*/
if ($this->content !== null) {
$html = static::htmlSpan($this->content, $this->property);
} else {
$html = static::htmlProperty($this->property);
}
break;
}
}
} elseif ($this->fallbackProperty) {
// Process and return the HTML the way the user expects to
if ($displayType) {
switch ($displayType) {
case 'span':
$html = static::htmlSpan($html, $this->fallbackProperty, $this->fallbackType);
break;
case 'div':
$html = static::htmlDiv($html, $this->fallbackProperty, $this->fallbackType);
break;
case 'meta':
$html = $this->machineContent ?? $html;
$html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
break;
default:
// Default $displayType = 'inline'
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
break;
}
} else {
/*
* Process and return the HTML in an automatic way,
* with the $Property expected Types and display everything in the right way,
* check if the Property is 'nested' or must be rendered in a metadata tag
*/
switch (static::getExpectedDisplayType($this->fallbackType, $this->fallbackProperty)) {
case 'meta':
// Check if a $content is available, otherwise fallback to an 'inline' display Type
if ($this->content !== null) {
$html = $this->machineContent ?? $this->content;
$html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
} else {
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
}
break;
default:
/*
* Default expected display type = 'normal'
* Check if a $content is available,
* otherwise fallback to an 'inline' display Type
*/
if ($this->content !== null) {
$html = static::htmlSpan($this->content, $this->fallbackProperty);
$html = static::htmlSpan($html, '', $this->fallbackType);
} else {
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
}
break;
}
}
} elseif (!$this->fallbackProperty && $this->fallbackType !== null) {
$html = static::htmlScope($this->fallbackType);
}
// Reset params
$this->resetParams();
return $html;
}
/**
* Return the HTML of the current Scope
*
* @return string
*
* @since 3.2
*/
public function displayScope()
{
// Control if the library output is enabled, otherwise return the $content or empty string
if (!$this->enabled) {
return '';
}
return static::htmlScope($this->type);
}
/**
* Return the sanitized $Type
*
* @param string $type The Type to sanitize
*
* @return string
*
* @since 3.2
*/
public static function sanitizeType($type)
{
return ucfirst(trim($type));
}
/**
* Return the sanitized $Property
*
* @param string $property The Property to sanitize
*
* @return string
*
* @since 3.2
*/
public static function sanitizeProperty($property)
{
return lcfirst(trim($property));
}
/**
* Return an array with all available Types and Properties from the http://schema.org vocabulary
*
* @return array
*
* @since 3.2
*/
public static function getTypes()
{
static::loadTypes();
return static::$types;
}
/**
* Return an array with all available Types from the http://schema.org vocabulary
*
* @return array
*
* @since 3.2
*/
public static function getAvailableTypes()
{
static::loadTypes();
return array_keys(static::$types);
}
/**
* Return the expected Types of the given Property
*
* @param string $type The Type to process
* @param string $property The Property to process
*
* @return array
*
* @since 3.2
*/
public static function getExpectedTypes($type, $property)
{
static::loadTypes();
$tmp = static::$types[$type]['properties'];
// Check if the $Property is in the $Type
if (isset($tmp[$property])) {
return $tmp[$property]['expectedTypes'];
}
// Check if the $Property is inherit
$extendedType = static::$types[$type]['extends'];
// Recursive
if (!empty($extendedType)) {
return static::getExpectedTypes($extendedType, $property);
}
return [];
}
/**
* Return the expected display type: [normal|nested|meta]
* In which way to display the Property:
* normal -> itemprop="name"
* nested -> itemprop="director" itemscope itemtype="https://schema.org/Person"
* meta -> `<meta itemprop="datePublished" content="1991-05-01">`
*
* @param string $type The Type where to find the Property
* @param string $property The Property to process
*
* @return string
*
* @since 3.2
*/
protected static function getExpectedDisplayType($type, $property)
{
$expectedTypes = static::getExpectedTypes($type, $property);
// Retrieve the first expected type
$type = $expectedTypes[0];
// Check if it's a 'meta' display
if ($type === 'Date' || $type === 'DateTime' || $property === 'interactionCount') {
return 'meta';
}
// Check if it's a 'normal' display
if ($type === 'Text' || $type === 'URL' || $type === 'Boolean' || $type === 'Number') {
return 'normal';
}
// Otherwise it's a 'nested' display
return 'nested';
}
/**
* Recursive function, control if the given Type has the given Property
*
* @param string $type The Type where to check
* @param string $property The Property to check
*
* @return boolean
*
* @since 3.2
*/
public static function isPropertyInType($type, $property)
{
if (!static::isTypeAvailable($type)) {
return false;
}
// Control if the $Property exists, and return 'true'
if (\array_key_exists($property, static::$types[$type]['properties'])) {
return true;
}
// Recursive: Check if the $Property is inherit
$extendedType = static::$types[$type]['extends'];
if (!empty($extendedType)) {
return static::isPropertyInType($extendedType, $property);
}
return false;
}
/**
* Control if the given Type class is available
*
* @param string $type The Type to check
*
* @return boolean
*
* @since 3.2
*/
public static function isTypeAvailable($type)
{
static::loadTypes();
return \array_key_exists($type, static::$types);
}
/**
* Return Microdata semantics in a `<meta>` tag with content for machines.
*
* @param string $content The machine content to display
* @param string $property The Property
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlMeta($content, $property, $scope = '', $invert = false)
{
return static::htmlTag('meta', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a `<span>` tag.
*
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlSpan($content, $property = '', $scope = '', $invert = false)
{
return static::htmlTag('span', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a `<div>` tag.
*
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlDiv($content, $property = '', $scope = '', $invert = false)
{
return static::htmlTag('div', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a specified tag.
*
* @param string $tag The HTML tag
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.3
*/
public static function htmlTag($tag, $content, $property = '', $scope = '', $invert = false)
{
// Control if the $Property has already the 'itemprop' prefix
if (!empty($property) && stripos($property, 'itemprop') !== 0) {
$property = static::htmlProperty($property);
}
// Control if the $Scope have already the 'itemscope' prefix
if (!empty($scope) && stripos($scope, 'itemscope') !== 0) {
$scope = static::htmlScope($scope);
}
// Depending on the case, the $scope must precede the $property, or otherwise
if ($invert) {
$tmp = implode(' ', [$property, $scope]);
} else {
$tmp = implode(' ', [$scope, $property]);
}
$tmp = trim($tmp);
$tmp = ($tmp) ? ' ' . $tmp : '';
// Control if it is an empty element without a closing tag
if ($tag === 'meta') {
return "<meta$tmp content='$content'>";
}
return '<' . $tag . $tmp . '>' . $content . '</' . $tag . '>';
}
/**
* Return the HTML Scope
*
* @param string $scope The Scope to process
*
* @return string
*
* @since 3.2
*/
public static function htmlScope($scope)
{
return "itemscope itemtype='https://schema.org/" . static::sanitizeType($scope) . "'";
}
/**
* Return the HTML Property
*
* @param string $property The Property to process
*
* @return string
*
* @since 3.2
*/
public static function htmlProperty($property)
{
return "itemprop='$property'";
}
}
The same content is in 6 releases, Joomla 4.2.7-rc1 to 4.3.0-alpha3. See them all, or every version of this file.