// return is_string($date) ? (new \DateTime($date, self::$utc))->diff($this->date)->days : $date->diff($this->date)->days;
}
/**
/**
* Wrapper around \DateTime->setDate() for the current date
* Wrapper around \DateTime->setDate() for the current date
*
*
@ -725,16 +756,33 @@ class Date implements ArrayAccess, IteratorAggregate
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
throw new \InvalidArgumentException('TODO: Property offsetGet["' . $name . '"] not implemented yet');
case 'yesterday': return (clone $this)->modify('-1 day');
case 'tomorrow': return (clone $this)->modify('+1 day');
case 'prevday': return (clone $this)->modify('-1 day');
case 'nextday': return (clone $this)->modify('+1 day');
case 'prev_day': return (clone $this)->modify('-1 day');
case 'next_day': return (clone $this)->modify('+1 day');
case 'prevweek': return (clone $this)->modify('-7 days');
case 'nextweek': return (clone $this)->modify('+7 days');
case 'prev_week': return (clone $this)->modify('-7 days');
case 'next_week': return (clone $this)->modify('+7 days');
case 'prevmonth': return (clone $this)->modify('-1 month');
case 'nextmonth': return (clone $this)->modify('+1 month');
case 'prev_month': return (clone $this)->modify('-1 month');
case 'next_month': return (clone $this)->modify('+1 month');
case 'prevyear': return (clone $this)->modify('-1 year');
case 'nextyear': return (clone $this)->modify('+1 year');
case 'prev_year': return (clone $this)->modify('-1 year');
case 'next_year': return (clone $this)->modify('+1 year');
// strtolower($name) versions
// strtolower($name) versions
case 'year': return $this->date->format('Y');
case 'year': return $this->date->format('Y');
case 'month': return $this->date->format('m');
case 'month': return $this->date->format('m');
case 'day': return $this->date->format('d');
case 'day': return $this->date->format('d');
case 'hour': return $this->date->format('G'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the hour for time. The range of the return value is 0 to 23 for time-of-day values. However, the range of TIME values actually is much larger, so HOUR can return values greater than 23.
case 'hour': return $this->date->format('G'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the hour for time. The range of the return value is 0 to 23 for time-of-day values. However, the range of TIME values actually is much larger, so HOUR can return values greater than 23.
case 'minute': return $this->date->format('i'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_minute MySQL: Returns the minute for time, in the range 0 to 59.
case 'minute': return $this->date->format('i'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_minute MySQL: Returns the minute for time, in the range 0 to 59.
case 'second': return $this->date->format('s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the second for time, in the range 0 to 59.
case 'second': return $this->date->format('s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the second for time, in the range 0 to 59.
}
}
throw new \InvalidArgumentException('Property offsetGet["' . $name . '"] does not exist!');
throw new \InvalidArgumentException('Property offsetGet["' . $name . '"] does not exist!');
@ -925,9 +973,27 @@ class Date implements ArrayAccess, IteratorAggregate
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
case 'age': return $this->date->format('Y-m-d');
case 'age': return (new \DateTime('now', self::$utc))->diff($this->date)->y;
case 'days': return (new \DateTime('now', self::$utc))->diff($this->date)->days;
throw new \InvalidArgumentException('TODO: Property ->' . $name . ' not implemented yet');
case 'yesterday': return (clone $this)->modify('-1 day');
case 'tomorrow': return (clone $this)->modify('+1 day');
case 'prevday': return (clone $this)->modify('-1 day');
case 'nextday': return (clone $this)->modify('+1 day');
case 'prev_day': return (clone $this)->modify('-1 day');
case 'next_day': return (clone $this)->modify('+1 day');
case 'prevweek': return (clone $this)->modify('-7 days');
case 'nextweek': return (clone $this)->modify('+7 days');
case 'prev_week': return (clone $this)->modify('-7 days');
case 'next_week': return (clone $this)->modify('+7 days');
case 'prevmonth': return (clone $this)->modify('-1 month');
case 'nextmonth': return (clone $this)->modify('+1 month');
case 'prev_month': return (clone $this)->modify('-1 month');
case 'next_month': return (clone $this)->modify('+1 month');
case 'prevyear': return (clone $this)->modify('-1 year');
case 'nextyear': return (clone $this)->modify('+1 year');
case 'prev_year': return (clone $this)->modify('-1 year');
case 'next_year': return (clone $this)->modify('+1 year');
// strtolower($name) versions
// strtolower($name) versions
case 'year': return $this->date->format('Y');
case 'year': return $this->date->format('Y');
@ -956,9 +1022,6 @@ class Date implements ArrayAccess, IteratorAggregate
case 'timestamp':
case 'timestamp':
$this->date->setTimestamp($value);
$this->date->setTimestamp($value);
break;
break;
case 'day':
case 'day':
}
}
throw new \InvalidArgumentException('Property ->' . $name . ' cannot be set!');
throw new \InvalidArgumentException('Property ->' . $name . ' cannot be set!');
'joins' => [ 'name' => 'LEFT JOIN nation_names %1$s ON %1$s.nation_id = n.id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
],
static $joins = array( 'name' => ' LEFT JOIN nation_names %1$s ON %1$s.nation_id = n.id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
'history' => ' LEFT JOIN nation_history nh ON nh.nation_id = n.id AND "%s" BETWEEN nh.from_date AND nh.until_date',
'capital' => ' LEFT JOIN city_names %1$s ON %1$s.city_id = nh.capital_id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
'territories' => ' LEFT JOIN territory_nations tn ON tn.territory_id = t.id',
'territories-admin' => ' LEFT JOIN territory t ON t.id = tn.territory_id',
'population' => ' LEFT JOIN nation_population np ON np.nation_id = n.id AND %s BETWEEN np.from_day AND np.until_day', // This uses a 'pre-calculated' day, ie. TO_DAYS(CURDATE) ... HOWEVER, We could also add anything we wanted to the string! ie. 'IFNULL(TO_DAYS("' . $date . '"), 0)' ... WARNING: TO_DAYS("0000-00-00") is converted to NULL!
// 'profile' => '', // common +
// 'city' => ' LEFT JOIN nations n ON n.id = tn.nation_id', // shouldn't the city create the joins ???
// 'wikipedia' => ' LEFT JOIN urls %1$s ON %1$s.object = "nation" AND %1$s.object_id = n.id AND %1$s.lang = "%2$s" AND %1$s.type = "wikipedia"', // DEPRECATED!
'modified-by' => ' LEFT JOIN users muid ON muid.id = n.modified_user_id'
);
public $contexts = [ 'profile' => [],
''
];
public $virtualFields = [ 'age' => 'TIMESTAMPDIFF(YEAR, u.dob, NOW()) as age',
'joins' => [ 'name' => 'LEFT JOIN nation_names %1$s ON %1$s.nation_id = n.id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
],
static $joins = array( 'name' => ' LEFT JOIN nation_names %1$s ON %1$s.nation_id = n.id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
'history' => ' LEFT JOIN nation_history nh ON nh.nation_id = n.id AND "%s" BETWEEN nh.from_date AND nh.until_date',
'capital' => ' LEFT JOIN city_names %1$s ON %1$s.city_id = nh.capital_id AND %1$s.lang = "%2$s" AND "%3$s" BETWEEN %1$s.from_date AND %1$s.until_date',
'territories' => ' LEFT JOIN territory_nations tn ON tn.territory_id = t.id',
'territories-admin' => ' LEFT JOIN territory t ON t.id = tn.territory_id',
'population' => ' LEFT JOIN nation_population np ON np.nation_id = n.id AND %s BETWEEN np.from_day AND np.until_day', // This uses a 'pre-calculated' day, ie. TO_DAYS(CURDATE) ... HOWEVER, We could also add anything we wanted to the string! ie. 'IFNULL(TO_DAYS("' . $date . '"), 0)' ... WARNING: TO_DAYS("0000-00-00") is converted to NULL!
// 'profile' => '', // common +
// 'city' => ' LEFT JOIN nations n ON n.id = tn.nation_id', // shouldn't the city create the joins ???
// 'wikipedia' => ' LEFT JOIN urls %1$s ON %1$s.object = "nation" AND %1$s.object_id = n.id AND %1$s.lang = "%2$s" AND %1$s.type = "wikipedia"', // DEPRECATED!
'modified-by' => ' LEFT JOIN users muid ON muid.id = n.modified_user_id'
);
public $contexts = [ 'profile' => [],
''
];
public $virtualFields = [ 'age' => 'TIMESTAMPDIFF(YEAR, u.dob, NOW()) as age',
if ($db->lookup('SELECT SQL_CACHE 1 FROM bot_bans WHERE ip = 0x' . $this->ip2hex))
{
$db->real_query('UPDATE bot_ban_requests SET requests = requests + 1 WHERE ip = 0x' . $this->ip2hex);
$db->close();
sleep(3);
header('HTTP/1.0 403 Forbidden');
exit;
}
}
private function _get_cc()
{
return $this->db->lookup( $this->is_ipv4 ?
('SELECT SQL_CACHE cc FROM geoip2_ipv4 WHERE ' . $this->ipv4 . ' BETWEEN range_from AND range_until') :
// 'SELECT SQL_CACHE cc FROM geoip2_ipv6 WHERE CONV(HEX(LEFT(0x' . self::$ip2hex . ', 8)), 16, 10) BETWEEN range_high_from AND range_high_until AND CONV(HEX(RIGHT(0x' . self::$ip2hex . ', 8)), 16, 10) BETWEEN range_low_from AND range_low_until LIMIT 1'
('SELECT SQL_CACHE cc FROM geoip2_ipv6 WHERE 0x' . substr($this->ip2hex, 0, 16) . ' BETWEEN range_high_from AND range_high_until AND 0x' . substr($this->ip2hex, 16) . ' BETWEEN range_low_from AND range_low_until LIMIT 1')
);
}
private function _get_agent_ex() // `agent` is just a generic term for `agent`, `forwarded for` and `via`
// TEMPORARY HACK, while we examine the various 'forwarded_for' values!
// I want to know which of the various `forwarded_for` combinations are used on the internet!
// So these are just `bitmasks` of various possible fields! Once we establish which ones are used, we can remove the bit fields and the $_SERVER[] array member in normalize_forwarded_for()
if ($field == 'forwarded_for')
// REMOVE THIS SECTION when our 'testing' is complete! We should determine what 'forwarded_for' server variables are used, and reduce it!
if (preg_match($regexp, $path, $this->params) === 1)
if (preg_match($regexp, $path, $this->params) === 1)
{
{
$this->route['regexp'] = $regexp; // store the `winning` regexp
$this->route['regexp'] = $regexp; // store the `winning` regexp
@ -177,7 +110,13 @@ class Request
}
}
else if (is_null($arr[0]) || strpos($arr[0], $method) !== false)
else if (is_null($arr[0]) || strpos($arr[0], $method) !== false)
{
{
$regexp = '~^' . (empty($arr[1]) ? '/' . $paths[1] : ((strpos($arr[1], '/') === 0 ? null : '/' . $paths[1] . '/') . $this->_convert_route_pattern($arr[1]))) . '$~'; // by default if we leave the route/command null, we use /login instead of /login/ like `/admin/...` basically this works differently to the array (sub-folder) version for arrays, where we append /name/ but here we only do /name
. $this->expandRoutePattern($arr[1]))) . '$~'; // by default if we leave the route/command null, we use /login instead of /login/ like `/admin/...` basically this works differently to the array (sub-folder) version for arrays, where we append /name/ but here we only do /name
if (preg_match($regexp, $path, $this->params) === 1)
if (preg_match($regexp, $path, $this->params) === 1)
{
{
$this->route['regexp'] = $regexp;
$this->route['regexp'] = $regexp;
@ -197,18 +136,26 @@ class Request
{
{
if (($pos = strpos($controller, '::')) !== false) // controller is a static class method
if (($pos = strpos($controller, '::')) !== false) // controller is a static class method
else if (($pos = strpos($controller, '->')) !== false) // controller is an instantiatable object, usually an object extending the base Controller class
else if (($pos = strpos($controller, '->')) !== false) // controller is an instantiatable object, usually an object extending the base Controller class
// This function expands routes like '/admin/article/{id}' ==> '/admin/article/(?<id>[0-9]+)'
// This function expands routes like '/admin/article/{id}' ==> '/admin/article/(?<id>[0-9]+)'
// It also converts TRAILING `optional` paths to the preg equivalent: '/club/{id}[/history]' ==> '/club/(?<id>[0-9]+)(?:/history)?'
// It also converts TRAILING `optional` paths to the preg equivalent: '/club/{id}[/history]' ==> '/club/(?<id>[0-9]+)(?:/history)?'
//
//
private function _convert_route_pattern($route)
public function expandRoutePattern($route)
{
{
if (strrpos($route, ']', -1) !== false) // check if the route ends with optional parameters (where last character in route is `]`)
if (strpos($route, ']', -1) !== false) // check if the route ends with optional parameters (where last character in route is `]`)
{
{
// Check matching number of opening & closing brackets ... disabled only for performance reasons, the preg_match() will also throw an exception!?!? wtf?
// Check matching number of opening & closing brackets ... disabled only for performance reasons, the preg_match() will also throw an exception!?!? wtf?
// eg. request::build_url(array('path' => '/login')) == http://host/login (NOTE: Any `default` (request::$query) query string will be ignored when `path` is specified!)
// eg. request::build_url(array('path' => '/login')) == http://host/login (NOTE: Any `default` (request::$query) query string will be ignored when `path` is specified!)