all-in-one-event-calendar/lib/parser/frequency.php
2017-11-09 17:36:04 +01:00

203 lines
5.8 KiB
PHP

<?php
/**
* Frequency parser.
*
* @author Time.ly Network, Inc.
* @since 2.0
* @instantiator new
* @package Ai1EC
* @subpackage Ai1EC.Parser
*/
class Ai1ec_Frequency_Utility {
/**
* @var array Map of default multipliers
*/
protected $_multipliers = array(
's' => 1, // take care, to always have an identifier with unit of `1`
'm' => 60,
'h' => 3600,
'd' => 86400,
'w' => 604800,
);
/**
* @var array Map of WordPress native multipliers
*/
protected $_wp_names = array(
'hourly' => array(
'item' => array( 'h' => 1 ),
'seconds' => 3600
),
'twicedaily' => array(
'item' => array( 'd' => 0.5 ),
'seconds' => 43200
),
'daily' => array(
'item' => array( 'd' => 1 ),
'seconds' => 86400
),
);
/**
* @var string One letter code for lowest available quantifier
*/
protected $_lowest_quantifier = 's';
/**
* @var array Parsed representation - quantifiers and their amounts
*/
protected $_parsed = array();
/**
* Inject different multiplier
*
* Add multiplier, to parseable characters
*
* @param string $letter Letter (single ASCII letter) to allow as quantifier
* @param int $quant Number of seconds quantifier represents
*
* @return Ai1ec_Frequency_Utility Instance of self for chaining
*
* @throws Ai1ec_Invalid_Argument_Exception If first argument is not
* an ASCII letter.
*/
public function add_multiplier( $letter, $quant ) {
$letter = substr( (string)$letter, 0, 1 );
$quant = (int)$quant;
if ( $quant < 0 || ! preg_match( '/^[a-z]$/i', $letter ) ) {
throw new Ai1ec_Invalid_Argument_Exception(
'First argument to add_multiplier must be ASCII letter' .
'(a-zA-Z), and second - an integer'
);
}
$this->_multipliers[$letter] = $quant;
return $this;
}
/**
* Parse user input
*
* Convert arbitrary user input (i.e. "2w 10h") to internal representation
*
* @param string $input User input for frequency
*
* @return bool Success
*/
public function parse( $input ) {
$input = strtolower(
preg_replace(
'|(\d*\.?\d+)\s+([a-z])|',
'$1$2',
trim( $input )
)
);
if ( isset( $this->_wp_names[$input] ) ) {
$this->_parsed = $this->_wp_names[$input]['item'];
return true;
}
$match = $this->_match( $input );
if ( ! $match ) {
return false;
}
$this->_parsed = $match;
return true;
}
/**
* Convert parsed input to corresponding seconds
*
* @return int Number of seconds corresponding to user input
*/
public function to_seconds() {
$seconds = 0;
foreach ( $this->_parsed as $quantifier => $number ) {
$seconds += $number * $this->_multipliers[$quantifier];
}
$seconds = (int)$seconds; // discard any fractional part
return $seconds;
}
/**
* Convert parsed input to unified format
*
* @return string Unified output format
*/
public function to_string() {
$seconds = $this->to_seconds();
if ( $wp_name = $this->match_wp_native_interval( $seconds ) ) {
return $wp_name;
}
$reverse_quant = array_flip( $this->_multipliers );
krsort( $reverse_quant );
$output = array();
foreach ( $reverse_quant as $duration => $quant ) {
if ( $duration > $seconds ) {
continue;
}
$modded = (int)( $seconds / $duration );
if ( $modded > 0 ) {
$output[] = $modded . $quant;
$seconds -= $modded * $duration;
if ( $seconds <= 0 ) {
break;
}
}
}
return implode( ' ', $output );
}
/**
* Returns seconds interval to native wp name,
*
* @param int $seconds Value.
*
* @return bool|string False or name.
*/
public function match_wp_native_interval( $seconds ) {
if ( empty( $this->_parsed ) ) {
return false;
}
$response = false;
foreach ( $this->_wp_names as $name => $interval ) {
if ( $interval['seconds'] === $seconds ) {
$response = $name;
break;
}
}
return $response;
}
/**
* Extract time identifiers from input string
*
* Given arbitrary string collects known identifiers preceeded by numeric
* value and counts these. For example, given input '2d 1h 2h' will yield
* an `array( 'd' => 2, 'h' => 3 )`, that is easy to parse.
*
* @param string $input User supplied input
*
* @return array Extracted time identifiers
*/
protected function _match( $input ) {
$regexp = '/(\d*\.?\d+)([' .
implode( '|', array_keys( $this->_multipliers ) ) .
'])?/';
$matches = NULL;
if ( ! preg_match_all( $regexp, $input, $matches ) ) {
return false;
}
$output = array();
foreach ( $matches[0] as $key => $value ) {
$quantifier = ( ! empty( $matches[2][$key] ) )
? $matches[2][$key]
: $this->_lowest_quantifier;
if ( ! isset( $output[$quantifier] ) ) {
$output[$quantifier] = 0;
}
$output[$quantifier] += $matches[1][$key];
}
return $output;
}
}