625 lines
19 KiB
PHP
625 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* @brief Friendly URL
|
|
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
|
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
|
* @license http://www.invisionpower.com/legal/standards/
|
|
* @package IPS Community Suite
|
|
* @since 11 May 2015
|
|
* @version SVN_VERSION_NUMBER
|
|
*/
|
|
|
|
namespace IPS\Http\Url;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Friendly URL
|
|
*/
|
|
class _Friendly extends \IPS\Http\Url\Internal
|
|
{
|
|
/**
|
|
* @brief Base
|
|
*/
|
|
public $base = 'front';
|
|
|
|
/**
|
|
* @brief SEO Template
|
|
*/
|
|
public $seoTemplate;
|
|
|
|
/**
|
|
* @brief SEO Titles (unencoded)
|
|
*/
|
|
public $seoTitles = array();
|
|
|
|
/**
|
|
* @brief The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
|
*/
|
|
public $friendlyUrlComponent;
|
|
|
|
/* !Factory methods */
|
|
|
|
/**
|
|
* Create a friendly URL from a query string
|
|
*
|
|
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
|
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
|
* @param array $queryString Additional query string data
|
|
* @return \IPS\Http\Url\Friendly
|
|
* @throws \IPS\Http\Url\Exception
|
|
*/
|
|
public static function friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )
|
|
{
|
|
if ( \IPS\Settings::i()->htaccess_mod_rewrite )
|
|
{
|
|
return static::createInternalFromComponents(
|
|
'front',
|
|
$protocol,
|
|
$friendlyUrlComponent ? "{$friendlyUrlComponent}/" : '',
|
|
$queryString
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return static::createInternalFromComponents(
|
|
'front',
|
|
$protocol,
|
|
'index.php',
|
|
( $friendlyUrlComponent ? array( "/{$friendlyUrlComponent}/" => NULL ) : array() ) + $queryString
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a friendly URL from a full URL, working out friendly URL data
|
|
*
|
|
* @param array $components An array of components as returned by componentsFromUrlString()
|
|
* @param string $potentialFurl The potential FURL slug (e.g. "topic/1-test")
|
|
* @return \IPS\Http\Url\Friendly|NULL
|
|
*/
|
|
public static function createFriendlyUrlFromComponents( $components, $potentialFurl )
|
|
{
|
|
/* Loop each of our friendly URL definitions */
|
|
$matchedFurlDefinition = NULL;
|
|
$seoTitles = array();
|
|
foreach ( static::furlDefinition() as $seoTemplate => $furlDefinition )
|
|
{
|
|
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $potentialFurl ) )
|
|
{
|
|
$matchedFurlDefinition = $furlDefinition;
|
|
list( $matchedParams, $seoTitles ) = $returnedMatches;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we didn't get one return NULL */
|
|
if ( !$matchedFurlDefinition )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* If we did, create our object... */
|
|
return static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] )
|
|
->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $potentialFurl );
|
|
}
|
|
|
|
/**
|
|
* Create a friendly URL from a query string with known friendly URL data
|
|
*
|
|
* @param string $queryString The query string
|
|
* @param string $seoTemplate The key for making this a friendly URL
|
|
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
|
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
|
* @return \IPS\Http\Url\Friendly
|
|
* @throws \IPS\Http\Url\Exception
|
|
* @todo Currently this will silently return a non-friendly URL if $seoTemplate is not valid, for backwards compatibility. Remove this in a major update.
|
|
*/
|
|
public static function friendlyUrlFromQueryString( $queryString, $seoTemplate, $seoTitles, $protocol )
|
|
{
|
|
/* Get the friendly URL component */
|
|
try
|
|
{
|
|
$friendlyUrlComponent = static::buildFriendlyUrlComponentFromData( $queryString, $seoTemplate, $seoTitles );
|
|
}
|
|
catch ( \IPS\Http\Url\Exception $e )
|
|
{
|
|
if ( $e->getMessage() === 'INVALID_SEO_TEMPLATE' and !\IPS\IN_DEV )
|
|
{
|
|
return static::createInternalFromComponents( 'front', $protocol, 'index.php', $queryString );
|
|
}
|
|
else
|
|
{
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/* Extract the hidden query string parameters inside it */
|
|
$matchedParams = array();
|
|
$furlDefinition = static::furlDefinition();
|
|
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
|
{
|
|
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
|
}
|
|
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition[ $seoTemplate ], $friendlyUrlComponent ) )
|
|
{
|
|
list( $matchedParams, $_seoTitles ) = $returnedMatches;
|
|
}
|
|
else
|
|
{
|
|
if ( \IPS\IN_DEV )
|
|
{
|
|
throw new \IPS\Http\Url\Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL' );
|
|
}
|
|
}
|
|
|
|
/* Return */
|
|
return static::friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $friendlyUrlComponent );
|
|
}
|
|
|
|
/* !Instance */
|
|
|
|
/**
|
|
* Set friendly URL data
|
|
*
|
|
* @param string $seoTemplate The key for making this a friendly URL
|
|
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
|
* @param array $matchedParams The values for hidden query string properties
|
|
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
|
* @return \IPS\Http\Url\Friendly
|
|
* @throws \IPS\Http\Url\Exception
|
|
*/
|
|
protected function setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams=array(), $friendlyUrlComponent )
|
|
{
|
|
/* Get the definition */
|
|
$furlDefinition = static::furlDefinition();
|
|
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
|
{
|
|
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
|
}
|
|
|
|
/* Set basic properties */
|
|
$this->seoTemplate = $seoTemplate;
|
|
$this->seoTitles = is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
|
$this->friendlyUrlComponent = $friendlyUrlComponent;
|
|
|
|
/* Set hidden query string */
|
|
parse_str( $furlDefinition[ $seoTemplate ]['real'], $hiddenQueryString );
|
|
$this->hiddenQueryString = $hiddenQueryString + $matchedParams;
|
|
|
|
/* Return */
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Validate that the SEO title is correct
|
|
*
|
|
* @return mixed The correct URL if incorrect, TRUE is correct, or NULL if unknown
|
|
*/
|
|
public function correctFriendlyUrl()
|
|
{
|
|
/* Get the definition */
|
|
$furlDefinition = static::furlDefinition();
|
|
|
|
/* If we don't have a validate callback, we can return NULL */
|
|
if ( !isset( $furlDefinition[ $this->seoTemplate ]['verify'] ) or !$furlDefinition[ $this->seoTemplate ]['verify'] )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Load it */
|
|
try
|
|
{
|
|
if ( $correctUrl = $this->correctUrlFromVerifyClass( $furlDefinition[ $this->seoTemplate ]['verify'] ) )
|
|
{
|
|
/* IP.Board 3.x used /page-x in the path rather than a query string argument - we support this so as not to break past links */
|
|
if( mb_strpos( (string) $this, '/page-' ) )
|
|
{
|
|
preg_match( "/\/page\-(\d+)/", (string) $this, $matches );
|
|
|
|
if( isset( $matches[1] ) )
|
|
{
|
|
$correctUrl = $correctUrl->setQueryString( 'page', $matches[1] );
|
|
}
|
|
}
|
|
|
|
/* IP.Board 3.x also used to do /page__x__y__a__b for /?x=y&a=b ... let's extract those parameters */
|
|
if( mb_strpos( (string) $this, '/page__' ) )
|
|
{
|
|
preg_match( "/\/page__([^\/$]+?)(?:$|\/)/", (string) $this, $matches );
|
|
|
|
if( isset( $matches[1] ) )
|
|
{
|
|
$params = explode( '__', $matches[1] );
|
|
$key = NULL;
|
|
|
|
foreach( $params as $param )
|
|
{
|
|
if( $key === NULL )
|
|
{
|
|
$key = $param;
|
|
}
|
|
else
|
|
{
|
|
$correctUrl = $correctUrl->setQueryString( $key, $param );
|
|
$key = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (string) $correctUrl->normaliseForEqualityCheck() !== (string) $this->normaliseForEqualityCheck() )
|
|
{
|
|
return $correctUrl;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
/* It doesn't exist */
|
|
catch ( \OutOfRangeException $e )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Normalise the URL for comparing if the "correct" URL is different to the accessed URL
|
|
*
|
|
* @return \IPS\Http\Url
|
|
*/
|
|
public function normaliseForEqualityCheck()
|
|
{
|
|
/* Strip the query string except the FURL component */
|
|
$return = $this->stripQueryString();
|
|
|
|
/* Make it schema-relative */
|
|
$return = $return->setScheme( NULL );
|
|
|
|
/* Return */
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Strip Query String
|
|
* Overrides main method so that the FURL slug isn't lost
|
|
*
|
|
* @param string|array $keys The key(s) to strip - if omitted, entire query string is wiped
|
|
* @return \IPS\Http\Url
|
|
*/
|
|
public function stripQueryString( $keys=NULL )
|
|
{
|
|
if ( $keys === NULL and !\IPS\Settings::i()->htaccess_mod_rewrite )
|
|
{
|
|
$keys = $this->queryString;
|
|
unset( $keys[ "/{$this->friendlyUrlComponent}/" ] );
|
|
$keys = array_keys( $keys );
|
|
}
|
|
|
|
return parent::stripQueryString( $keys );
|
|
}
|
|
|
|
/* !Utilities */
|
|
|
|
/**
|
|
* Get friendly URL component (e.g. "topic/1-test") from a query string and SEO template
|
|
*
|
|
* @param string $queryString The query string - is passed by reference and any parts used are removed, which can be used to detect extraneous parts
|
|
* @param string $seoTemplate The key for making this a friendly URL
|
|
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
|
* @return string
|
|
* @throws \IPS\Http\Url\Exception
|
|
*/
|
|
public static function buildFriendlyUrlComponentFromData( &$queryString, $seoTemplate, $seoTitles )
|
|
{
|
|
/* Get the definition */
|
|
$furlDefinition = static::furlDefinition();
|
|
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
|
{
|
|
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
|
}
|
|
$component = $furlDefinition[ $seoTemplate ]['friendly'];
|
|
|
|
/* Parse the query string into an array */
|
|
parse_str( $queryString, $queryString );
|
|
|
|
/* For each query string component, replace it out */
|
|
foreach ( $queryString as $k => $v )
|
|
{
|
|
if ( mb_strpos( $component, "{#{$k}}" ) !== FALSE or mb_strpos( $component, "{@{$k}}" ) !== FALSE )
|
|
{
|
|
$component = str_replace( "{#{$k}}", intval( $v ), $component );
|
|
$component = str_replace( "{@{$k}}", $v, $component );
|
|
unset( $queryString[ $k ] );
|
|
}
|
|
}
|
|
|
|
/* Parse out the titles */
|
|
$seoTitles = is_null( $seoTitles ) ? array() : ( is_string( $seoTitles ) ? array( $seoTitles ) : array_values( $seoTitles ) );
|
|
$seoTitlesCount = count( $seoTitles );
|
|
for ( $i = 0; $i < $seoTitlesCount; $i++ )
|
|
{
|
|
if ( $i === 0 )
|
|
{
|
|
$component = str_replace( '{?}', $seoTitles[ $i ], $component );
|
|
}
|
|
$component = str_replace( "{?{$i}}", $seoTitles[ $i ], $component );
|
|
}
|
|
|
|
/* Remove the "real" parts in the query string */
|
|
parse_str( $furlDefinition[ $seoTemplate ]['real'], $realQueryString );
|
|
foreach ( $realQueryString as $k => $v )
|
|
{
|
|
if ( isset( $queryString[ $k ] ) and $queryString[ $k ] == $v )
|
|
{
|
|
unset( $queryString[ $k ] );
|
|
}
|
|
}
|
|
|
|
/* Return */
|
|
return $component;
|
|
}
|
|
|
|
/**
|
|
* Convert a value into an "SEO Title" for friendly URLs
|
|
*
|
|
* @param string $value Value
|
|
* @return string
|
|
* @note Many places require an SEO title, so we always need to return something, so when no valid title is available we return a dash
|
|
*/
|
|
public static function seoTitle( $value )
|
|
{
|
|
/* Ensure there are no HTML tags */
|
|
$value = strip_tags( $value );
|
|
|
|
/* Always lowercase */
|
|
$value = mb_strtolower( $value );
|
|
|
|
/* Get rid of newlines/carriage returns as they're not cool in friendly URL titles */
|
|
$value = str_replace( array( "\r\n", "\r", "\n" ), ' ', $value );
|
|
|
|
/* Just for readability */
|
|
$value = str_replace( ' ', '-', $value );
|
|
|
|
/* Disallowed characters which browsers may try to automatically percent-encode */
|
|
$value = str_replace( array( '!', '*', '\'', '(', ')', ';', ':', '@', '&', '=', '+', '$', ',', '/', '?', '#', '[', ']', '%', '\\', '"', '<', '>', '^', '{', '}', '|', '.', '`' ), '', $value );
|
|
|
|
/* Trim */
|
|
$value = preg_replace( '/\-+/', '-', $value );
|
|
$value = trim( $value, '-' );
|
|
$value = trim( $value );
|
|
|
|
/* Return */
|
|
return $value ?: '-';
|
|
}
|
|
|
|
/**
|
|
* Get the matched parameters and SEO titles from a friendly URL component for a particular
|
|
*
|
|
* @param array $furlDefinition The FURL definition from the furl.json file
|
|
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
|
* @return array|NULL array( $matchedParams, $seoTitles ) if matched, NULL if the $friendlyUrlComponent does not match this $furlDefinition
|
|
*/
|
|
protected static function getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $friendlyUrlComponent )
|
|
{
|
|
/* See if it matches */
|
|
$seoTitles = array();
|
|
$matchedParams = array();
|
|
foreach ( $furlDefinition['regex'] as $_regex )
|
|
{
|
|
if ( preg_match( '/^' . $_regex . '$/i', $friendlyUrlComponent, $matches ) )
|
|
{
|
|
foreach ( $furlDefinition['params'] as $k => $param )
|
|
{
|
|
if ( $param )
|
|
{
|
|
$matchedParams[ $param ] = $matches[ $k + 1 ];
|
|
}
|
|
else
|
|
{
|
|
$seoTitles[] = $matches[ $k + 1 ];
|
|
}
|
|
}
|
|
|
|
return array( $matchedParams, $seoTitles );
|
|
}
|
|
}
|
|
|
|
/* Still here? No match */
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief FURL Definition
|
|
*/
|
|
protected static $furlDefinition = NULL;
|
|
|
|
/**
|
|
* Get FURL Definition
|
|
*
|
|
* @param bool $revert If TRUE, ignores all customisations and reloads from json
|
|
* @return array
|
|
*/
|
|
public static function furlDefinition( $revert=FALSE )
|
|
{
|
|
if ( static::$furlDefinition === NULL or $revert )
|
|
{
|
|
$furlCustomizations = ( \IPS\Settings::i()->furl_configuration AND !$revert ) ? json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) : array();
|
|
$furlConfiguration = ( isset( \IPS\Data\Store::i()->furl_configuration ) AND \IPS\Data\Store::i()->furl_configuration ) ? json_decode( \IPS\Data\Store::i()->furl_configuration, TRUE ) : array();
|
|
|
|
if ( ( \IPS\IN_DEV and !\IPS\DEV_USE_FURL_CACHE ) or !count( $furlConfiguration ) or $revert )
|
|
{
|
|
$furlConfiguration = static::buildFurlConfiguation();
|
|
}
|
|
|
|
static::$furlDefinition = array_merge( $furlConfiguration, $furlCustomizations );
|
|
}
|
|
|
|
return static::$furlDefinition;
|
|
}
|
|
|
|
/**
|
|
* Rebuild and return \IPS\Data\Store::i()->furl_configuration with default, uncustomised values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function buildFurlConfiguation()
|
|
{
|
|
/* Init */
|
|
$furlConfiguration = array();
|
|
|
|
/* Load apps, prioritising the default (otherwise if two apps both have "/category/..." the app which is higher in the list may steal from the default app) */
|
|
$applications = \IPS\Application::applications();
|
|
foreach ( $applications as $k => $app )
|
|
{
|
|
if ( $app->default )
|
|
{
|
|
unset( $applications[ $k ] );
|
|
array_unshift( $applications, $app );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Loop each app... */
|
|
foreach ( $applications as $app )
|
|
{
|
|
/* If it has a furl.json file... */
|
|
if( file_exists( \IPS\ROOT_PATH . "/applications/{$app->directory}/data/furl.json" ) )
|
|
{
|
|
/* Open it up */
|
|
$data = json_decode( preg_replace( '/\/\*.+?\*\//s', '', \file_get_contents( \IPS\ROOT_PATH . "/applications/{$app->directory}/data/furl.json" ) ), TRUE );
|
|
$topLevel = $data['topLevel'];
|
|
|
|
/* Process them */
|
|
$definitions = array();
|
|
foreach ( $data['pages'] as $k => $page )
|
|
{
|
|
$definitions[ $k ] = static::buildFurlDefinition( $page['friendly'], $page['real'], $topLevel, $app->default, isset( $page['alias'] ) ? $page['alias'] : NULL, FALSE, isset( $page['verify'] ) ? $page['verify'] : NULL );
|
|
}
|
|
|
|
/* Add it in */
|
|
$furlConfiguration = array_merge( $furlConfiguration, $definitions );
|
|
}
|
|
}
|
|
|
|
/* Store */
|
|
\IPS\Data\Store::i()->furl_configuration = json_encode( $furlConfiguration );
|
|
|
|
/* Return */
|
|
return $furlConfiguration;
|
|
}
|
|
|
|
/**
|
|
* Build the friendly URL definition
|
|
*
|
|
* @param string $friendly Friendly URL pattern
|
|
* @param string $real Non-friendly URL pattern
|
|
* @param NULL|string $appTopLevel FURL slug if the app is not the default app
|
|
* @param bool $appIsDefault Flag to indicate if the app is default or not
|
|
* @param NULL|string $alias Friendly URL alias
|
|
* @param bool $custom Flag to indicate if this is a custom FURL definition
|
|
* @param string $verify The name of a class that contains a loadFromUrl() and an url() method for verifying the friendly URL is correct
|
|
* @return array
|
|
*/
|
|
public static function buildFurlDefinition( $friendly, $real, $appTopLevel = NULL, $appIsDefault = FALSE, $alias = NULL, $custom = FALSE, $verify = NULL )
|
|
{
|
|
/* Init */
|
|
$return = array(
|
|
'friendly' => $friendly,
|
|
'real' => $real
|
|
);
|
|
if ( $verify )
|
|
{
|
|
$return['verify'] = $verify;
|
|
}
|
|
if ( $custom )
|
|
{
|
|
$return['custom'] = TRUE;
|
|
}
|
|
|
|
/* If it has a top-level (e.g. "/forums") we need to store the definition either with that if it's the default app, or
|
|
without it if it isn't, so that if the default app changes we can redirect accordingly */
|
|
if ( $appTopLevel )
|
|
{
|
|
if ( $appIsDefault )
|
|
{
|
|
$return['with_top_level'] = $appTopLevel . ( $friendly ? '/' . $friendly : '' );
|
|
}
|
|
else
|
|
{
|
|
$return['without_top_level'] = $friendly;
|
|
$return['friendly'] = $appTopLevel . ( $friendly ? '/' . $friendly : '' );
|
|
}
|
|
}
|
|
|
|
/* Figure out the regexes */
|
|
$return['regex'] = array( preg_quote( $return['friendly'], '/' ) );
|
|
if ( $alias )
|
|
{
|
|
$return['regex'][] = str_replace( '\{\!\}', '(?!&)(?:.+?)', preg_quote( $alias, '/' ) );
|
|
}
|
|
if ( isset( $return['without_top_level'] ) and $return['without_top_level'] )
|
|
{
|
|
$return['regex'][] = preg_quote( $return['without_top_level'], '/' );
|
|
}
|
|
elseif ( isset( $return['with_top_level'] ) )
|
|
{
|
|
$return['regex'][] = preg_quote( $return['with_top_level'], '/' );
|
|
}
|
|
|
|
/* Parse out variables */
|
|
$return['params'] = array();
|
|
preg_match_all( '/{(.+?)}/', $return['friendly'], $matches );
|
|
foreach ( $matches[1] as $tag )
|
|
{
|
|
switch ( mb_substr( $tag, 0, 1 ) )
|
|
{
|
|
case '#':
|
|
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
|
return str_replace( '\{#' . mb_substr( $tag, 1 ) . '\}', '(\d+?)', $_regex );
|
|
}, $return['regex'] );
|
|
$return['params'][] = mb_substr( $tag, 1 );
|
|
break;
|
|
|
|
case '@':
|
|
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
|
return str_replace( '\{@' . mb_substr( $tag, 1 ) . '\}', '(.+?)', $_regex );
|
|
}, $return['regex'] );
|
|
$return['params'][] = mb_substr( $tag, 1 );
|
|
break;
|
|
|
|
case '?':
|
|
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
|
return str_replace( '\{\?\}', '(?!&)(.+?)', $_regex );
|
|
}, $return['regex'] );
|
|
$return['params'][] = '';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return */
|
|
return $return;
|
|
}
|
|
|
|
/* !Deprecated */
|
|
|
|
/**
|
|
* @brief Is internal?
|
|
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Internal
|
|
*/
|
|
public $isInternal = TRUE;
|
|
|
|
/**
|
|
* @brief Is friendly?
|
|
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Friendly
|
|
*/
|
|
public $isFriendly = TRUE;
|
|
} |