Browse Source

Expanded ORM

master
therselman 7 years ago
parent
commit
ad0979cb3c
  1. 91
      src/Date.php
  2. 84
      src/ORM/Collection.php
  3. 8
      src/ORM/ComplexMap.php
  4. 11
      src/ORM/ComplexMapper.php
  5. 11
      src/ORM/DbDriver.php
  6. 30
      src/ORM/Entity.php
  7. 97
      src/ORM/EntityMap.php
  8. 102
      src/ORM/Mapper.php
  9. 13
      src/ORM/Repository.php
  10. 16
      src/ORM/SimpleMap.php
  11. 16
      src/ORM/SimpleMapper.php
  12. 132
      src/Request.php
  13. 2
      src/Response.php

91
src/Date.php

@ -43,7 +43,7 @@ class Date implements ArrayAccess, IteratorAggregate @@ -43,7 +43,7 @@ class Date implements ArrayAccess, IteratorAggregate
*
* @param \DateTime|string|null $date
*/
public function __construct($date)
public function __construct($date = 'now')
{
if (is_string($date))
{
@ -57,6 +57,10 @@ class Date implements ArrayAccess, IteratorAggregate @@ -57,6 +57,10 @@ class Date implements ArrayAccess, IteratorAggregate
{
$this->date = new \DateTime((string) $date, self::$utc);
}
else if ($date === null)
{
$this->date = new \DateTime('now', self::$utc);
}
else if (is_array($date))
{
throw new InvalidArgumentException('Array constructor not implemented');
@ -114,6 +118,33 @@ class Date implements ArrayAccess, IteratorAggregate @@ -114,6 +118,33 @@ class Date implements ArrayAccess, IteratorAggregate
}
/**
* Returns the difference between two dates in days
*/
public function days($date = null)
{
return is_string($date) ? (new \DateTime($date, self::$utc))->diff($this->date)->days : $date->diff($this->date)->days;
}
/**
* Returns the difference between two dates
*
* @link http://php.net/manual/en/datetime.diff.php
*
*/
public function diff($date = 'now')
{
if ($date instanceof static)
return $this->date->diff($date->date);
else if ($date instanceof \DateTime)
return $this->date->diff($date);
else if (is_string($date))
return $this->date->diff(new \DateTime($date, self::$utc));
// 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
*
@ -725,16 +756,33 @@ class Date implements ArrayAccess, IteratorAggregate @@ -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 '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
case 'year': return $this->date->format('Y');
case 'month': return $this->date->format('m');
case 'day': return $this->date->format('d');
case 'year': return $this->date->format('Y');
case 'month': return $this->date->format('m');
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 '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 '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 '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!');
@ -925,9 +973,27 @@ class Date implements ArrayAccess, IteratorAggregate @@ -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 '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');
throw new \InvalidArgumentException('TODO: Property ->' . $name . ' not implemented yet');
case 'age': return (new \DateTime('now', self::$utc))->diff($this->date)->y;
case 'days': return (new \DateTime('now', self::$utc))->diff($this->date)->days;
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
case 'year': return $this->date->format('Y');
@ -956,9 +1022,6 @@ class Date implements ArrayAccess, IteratorAggregate @@ -956,9 +1022,6 @@ class Date implements ArrayAccess, IteratorAggregate
case 'timestamp':
$this->date->setTimestamp($value);
break;
case 'day':
case 'day':
}
throw new \InvalidArgumentException('Property ->' . $name . ' cannot be set!');

84
src/ORM/Collection.php

@ -4,17 +4,22 @@ @@ -4,17 +4,22 @@
* Collection class with dynamic members
* Similar functionality to an Array or Dictionary class
* Typically holding a collection/array of Entity members
* This class is not particularly useful compared to a standard array,
* but it's used to `extend` the functionality of standard arrays.
*
*
* @link https://laravel.com/docs/5.4/eloquent-collections#available-methods
*
* @link http://php.net/manual/en/class.iterator.php
* @link http://php.net/manual/en/class.arrayaccess.php
* @link http://php.net/manual/en/class.countable.php
* @link http://php.net/manual/en/class.iteratoraggregate.php
* @link http://php.net/manual/en/class.jsonserializable.php
*
*
* @author Trevor Herselman <therselman@gmail.com>
*/
namespace Twister\ORM;
class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggregate //, JsonSerializable http://php.net/JsonSerializable
class Collection implements \Iterator, \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
{
protected $members = null;
@ -25,7 +30,7 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg @@ -25,7 +30,7 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg
/**
* Countable interface
*
*/
public function find(...$params)
{
@ -315,54 +320,6 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg @@ -315,54 +320,6 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg
}
/**
* Get an iterator for the members.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->members);
}
/**
* Get member by id/index
*
* Note: This is not very useful, because most members will be indexed by integer.
*
* @param string|int $idx
* @return mixed
*/
public function __get($idx)
{
return $this->members[$idx];
}
/**
* Set member by id/index
*
* @param string|int $idx
* @param mixed $value
* @return void
*/
public function __set($idx, $value)
{
$this->members[$idx] = $value;
}
function __isset($idx)
{
return isset($this->members[$idx]);
}
function __unset($idx)
{
unset($this->members[$idx]);
}
/**
* Get the collection of items as a plain array.
*
@ -391,11 +348,11 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg @@ -391,11 +348,11 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg
/**
*
* JsonSerializable interface
*/
public function toJson()
public function jsonSerialize()
{
return json_encode($this->members);
return $this->members;
}
@ -405,6 +362,21 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg @@ -405,6 +362,21 @@ class Collection implements \Iterator, \Countable, \ArrayAccess, \IteratorAggreg
}
/**
* Get an external iterator to the members.
*
* IteratorAggregate interface
*
* @link http://php.net/manual/en/class.iteratoraggregate.php
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->members);
}
/**
* Iterator interface
*/

8
src/ORM/ComplexMap.php

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
<?php
namespace Twister\ORM;
abstract class ComplexMap
{
}

11
src/ORM/ComplexMapper.php

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
<?php
namespace Twister\ORM;
class ComplexMapper
{
function __construct(ComplexMap $map)
{
}
}

11
src/ORM/DbDriver.php

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
<?php
namespace Twister\ORM;
/**
* Mappter = EntityMap + DbDriver
*
*/
interface DBDriver
{
}

30
src/ORM/Entity.php

@ -2,15 +2,24 @@ @@ -2,15 +2,24 @@
namespace Twister\ORM;
abstract class Entity
/**
* Similar Examples
* ----------------
* @link https://github.com/analogueorm/analogue/blob/5.5/src/Entity.php
*
* Interfaces
* ----------
* @link http://php.net/JsonSerializable
*/
abstract class Entity implements \JsonSerializable
{
protected $properties;
public $id;
protected $map; // AKA config; can include relations, factory methods for relations etc.
function __construct(array $properties = null)
{
$this->properties =& $properties;
$this->id = $properties['id'] ?? null;
}
/**
@ -21,9 +30,19 @@ abstract class Entity @@ -21,9 +30,19 @@ abstract class Entity
*/
function __get($name)
{
if ( ! isset($this->properties[$name]))
{
if (isset($this->map[$name]))
{
}
}
return $this->properties[$name];
}
// abstract private relation($name);
// abstract private __get__();
/**
* Set entity field/property
*
@ -44,4 +63,9 @@ abstract class Entity @@ -44,4 +63,9 @@ abstract class Entity
{
unset($this->properties[$name]);
}
function jsonSerialize()
{
return $this->properties;
}
}

97
src/ORM/EntityMap.php

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
<?php
namespace Twister\ORM;
/**
* `Maps` persistent storage to Entities
*
* Actions and responsibilities include:
* Implements field Mutators and
* Relationships & Joins
* List of Fields
* Table aliases
* Virtual / dynamic field names
*
*/
abstract class EntityMap
{
protected $fields;
protected $from;
protected $joins; // optional
protected $properties;
protected $aliases;
protected $relationships;
// abstract private __get__();
function __construct(array $properties = null)
{
$this->properties =& $properties;
}
}
/*
'aliases' => [ 'w' => 'worlds',
'u' => 'users',
'c' => 'clubs'
];
'keys' = [ [['u.id', 'w.user_id'], 'one-to-many'],
[['w.id', 'c.world_id'], 'one-to-many']
];
// dynamic optional field names
'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',
'style_name' => 'STYLIZE_NAME(u.fname, u.alias, u.lname, 0, 0)'
];
public static function factory()
{
}
Laravel
-------
$this->hasMany('App\User')->withTimestamps();
Analogue
--------
use Analogue\ORM\Relationships\BelongsTo;
use Analogue\ORM\Relationships\BelongsToMany;
use Analogue\ORM\Relationships\EmbedsMany;
use Analogue\ORM\Relationships\EmbedsOne;
use Analogue\ORM\Relationships\HasMany;
use Analogue\ORM\Relationships\HasManyThrough;
use Analogue\ORM\Relationships\HasOne;
use Analogue\ORM\Relationships\MorphMany;
use Analogue\ORM\Relationships\MorphOne;
use Analogue\ORM\Relationships\MorphTo;
use Analogue\ORM\Relationships\MorphToMany;
*/

102
src/ORM/Mapper.php

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
<?php
namespace Twister\ORM;
/**
* Extended by SimpleMapper and ComplexMapper
*
* Reads & processes an EntityMap + DB Driver/Adapter
*
* Actions and responsibilities include:
* Implements field Mutators and
* Relationships & Joins
* List of Fields
* Table aliases
* Virtual / dynamic field names
*
*/
abstract class Mapper
{
protected $fields;
protected $from;
protected $joins; // optional
protected $properties;
protected $aliases;
protected $relationships;
function __construct(array $properties = null)
{
$this->properties =& $properties;
}
abstract function select();
abstract function from();
// abstract private __get__();
}
/*
'aliases' => [ 'w' => 'worlds',
'u' => 'users',
'c' => 'clubs'
];
'keys' = [ [['u.id', 'w.user_id'], 'one-to-many'],
[['w.id', 'c.world_id'], 'one-to-many']
];
// dynamic optional field names
'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',
'style_name' => 'STYLIZE_NAME(u.fname, u.alias, u.lname, 0, 0)'
];
public static function factory()
{
}
Laravel
-------
$this->hasMany('App\User')->withTimestamps();
Analogue
--------
use Analogue\ORM\Relationships\BelongsTo;
use Analogue\ORM\Relationships\BelongsToMany;
use Analogue\ORM\Relationships\EmbedsMany;
use Analogue\ORM\Relationships\EmbedsOne;
use Analogue\ORM\Relationships\HasMany;
use Analogue\ORM\Relationships\HasManyThrough;
use Analogue\ORM\Relationships\HasOne;
use Analogue\ORM\Relationships\MorphMany;
use Analogue\ORM\Relationships\MorphOne;
use Analogue\ORM\Relationships\MorphTo;
use Analogue\ORM\Relationships\MorphToMany;
*/

13
src/ORM/Repository.php

@ -9,9 +9,18 @@ namespace Twister\ORM; @@ -9,9 +9,18 @@ namespace Twister\ORM;
* This class is designed for inheritance and users can subclass this class to
* write their own repositories with business-specific methods to locate entities.
*
*
* Similar Examples
* ----------------
* @link https://github.com/analogueorm/analogue/blob/5.5/src/Repository.php
*
* Interfaces
* ----------
* @link http://php.net/JsonSerializable
*
* @author Trevor Herselman <therselman@gmail.com>
*/
class Repository
abstract class Repository
{
/**
* The database connection used by the EntityManager.
@ -20,6 +29,8 @@ class Repository @@ -20,6 +29,8 @@ class Repository
*/
protected $em = null;
protected $mapper = null;
public function __construct(Container $c)
{

16
src/ORM/SimpleMap.php

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?php
namespace Twister\ORM;
abstract class SimpleMap
{
function __construct()
{
}
function build()
{
return new SimpleMapper(new static());
}
}

16
src/ORM/SimpleMapper.php

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?php
namespace Twister\ORM;
class SimpleMapper
{
function __construct(SimpleMap $map)
{
}
static function build(SimpleMap $map)
{
}
}

132
src/Request.php

@ -5,7 +5,6 @@ namespace Twister; @@ -5,7 +5,6 @@ namespace Twister;
class Request
{
private $container = null;
private $db = null;
public $remote_addr = null;
public $inet_pton = null;
@ -21,14 +20,8 @@ class Request @@ -21,14 +20,8 @@ class Request
public $isIpv4 = null;
public $is_ipv4 = null;
public $cc = null;
public $uri = null;
public $agent_id = null;
public $forwarded_for_id = null;
public $via_id = null;
public $route = null;
public $routes = null;
public $params = null;
@ -39,17 +32,12 @@ class Request @@ -39,17 +32,12 @@ class Request
$this->db = $container->db;
$this->_normalize_ip_address();
$this->_detect_banned_ips();
$this->method = strtoupper($_SERVER['REQUEST_METHOD']);
$this->uri = Uri::fromGlobals();
$this->isHttps = $this->is_https = $this->isSecure = $this->uri->isHttps();
$this->cc = $this->_get_cc();
$this->_get_agent_ex();
}
private function _normalize_ip_address()
@ -65,41 +53,6 @@ class Request @@ -65,41 +53,6 @@ class Request
$this->isIpv4 = $this->is_ipv4 = $this->ipv4 !== false;
}
private function _detect_banned_ips()
{
$db = $this->db;
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`
{
$db = $this->db;
$agent = isset($_SERVER['HTTP_USER_AGENT']) ? db::varchar($_SERVER['HTTP_USER_AGENT'], 700) : null;
$ff = db::varchar(self::_normalize_forwarded_for(), 128);
$via = isset($_SERVER['HTTP_VIA']) ? db::varchar($_SERVER['HTTP_VIA'], 224) : null;
$db->real_query('LOCK TABLES request_agents WRITE, request_forwarded_for WRITE, request_via WRITE');
$this->agent_id = empty($agent) ? 0 : $this->_get_agent_ex_id($db, $agent, 'request_agents', 'agent');
$this->forwarded_for_id = empty($ff) ? 0 : $this->_get_agent_ex_id($db, $ff, 'request_forwarded_for', 'forwarded_for');
$this->via_id = empty($via) ? 0 : $this->_get_agent_ex_id($db, $via, 'request_via', 'via');
$db->real_query('UNLOCK TABLES');
}
// Combine all the possible values for 'HTTP_X_FORWARDED_FOR' together
// List taken from: http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
private static function _normalize_forwarded_for()
@ -120,27 +73,7 @@ class Request @@ -120,27 +73,7 @@ class Request
return preg_replace('/[, ]+/', ',', implode(',', $result));
}
private function _get_agent_ex_id($db, $value, $table, $field)
{
$md5 = md5($value);
$id = $db->lookup('SELECT id FROM ' . $table . ' WHERE hash = 0x' . $md5);
if ( ! $id)
{
$id = $db->call('CALL spGetRandomID("id", "' . $table . '", 1, 0x7FFFFFFF)');
// 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!
$db->real_query('INSERT IGNORE INTO request_forwarded_for (id, hash, forwarded_for, client_ip, x_forwarded_for, x_forwarded, x_cluster_client_ip, forwarded_for2, forwarded) VALUES (' . $id . ', 0x' . $md5 . ', ' . $db->escape($value) . ', ' . (int) empty($_SERVER['HTTP_CLIENT_IP']) . ', ' . (int) empty($_SERVER['HTTP_X_FORWARDED_FOR']) . ', ' . (int) empty($_SERVER['HTTP_X_FORWARDED']) . ', ' . (int) empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) . ', ' . (int) empty($_SERVER['HTTP_FORWARDED_FOR']) . ', ' . (int) empty($_SERVER['HTTP_FORWARDED']) . ')');
else
// Leave only this statement after we've tested the various 'forwarded_for' combinations!
$db->real_query('INSERT IGNORE INTO ' . $table . ' (id, hash, ' . $field . ') VALUES (' . $id . ', 0x' . $md5 . ', ' . $db->escape($value) . ')');
}
return $id;
}
public function execute_route(array $routes)
public function execute_route(array $routes) // AKA dispach / dispatchRoute
{
$path = $this->uri->path;
$paths = explode('/', $path, 4);
@ -163,7 +96,7 @@ class Request @@ -163,7 +96,7 @@ class Request
{
if ( ! isset($route[0]) || strpos($route[0], $method) !== false)
{
$regexp = '~^' . (strpos($route[1], '/') === 0 ? null : '/' . $paths[1] . '/') . $this->_convert_route_pattern($route[1]) . '$~';
$regexp = '~^' . (strpos($route[1], '/') === 0 ? null : '/' . $paths[1] . '/') . $this->expandRoutePattern($route[1]) . '$~';
if (preg_match($regexp, $path, $this->params) === 1)
{
$this->route['regexp'] = $regexp; // store the `winning` regexp
@ -177,7 +110,13 @@ class Request @@ -177,7 +110,13 @@ class Request
}
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
//dump($arr);
///dump($method);
//dump($paths);
//dump($this->expandRoutePattern($arr[1]));
//die();
$regexp = '~^' . (empty($arr[1]) ? '/' . $paths[1] : ((strpos($arr[1], '/') === 0 ? null : '/' . $paths[1] . '/')
. $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)
{
$this->route['regexp'] = $regexp;
@ -197,18 +136,26 @@ class Request @@ -197,18 +136,26 @@ class Request
{
if (($pos = strpos($controller, '::')) !== false) // controller is a static class method
{
$class = substr($controller, 0, $pos);
$method = substr($controller, $pos + 2);
// $class = substr($controller, 0, $pos);
// $method = substr($controller, $pos + 2);
// require __DIR__ . '/../controllers/' . $class . '.php';
$reflection = new \ReflectionMethod($class, $method);
/**
* NOTE: If you get a `ReflectionException` here; with `Class MyClass does not exist`
* Then check the previous exception message for compile time PHP errors loading the class file!
*/
try {
$reflection = new \ReflectionMethod(substr($controller, 0, $pos), substr($controller, $pos + 2));
} catch (\ReflectionException $e) {
throw $e->getPrevious();
}
return $reflection->invokeArgs(null, $this->_get_args_from_params($reflection->getParameters()));
}
else if (($pos = strpos($controller, '->')) !== false) // controller is an instantiatable object, usually an object extending the base Controller class
{
$class = substr($controller, 0, $pos);
$method = substr($controller, $pos + 2);
// $class = substr($controller, 0, $pos);
// $method = substr($controller, $pos + 2);
// require __DIR__ . '/../controllers/' . $class . '.php';
$reflection = new \ReflectionMethod($class, $method);
$reflection = new \ReflectionMethod(substr($controller, 0, $pos), substr($controller, $pos + 2));
$obj = new $class($this->container);
return $reflection->invokeArgs($obj, $this->_get_args_from_params($reflection->getParameters()));
}
@ -259,9 +206,9 @@ class Request @@ -259,9 +206,9 @@ class Request
// 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)?'
//
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?
//if (substr_count($route, '[') !== substr_count($route, ']'))
@ -301,38 +248,21 @@ class Request @@ -301,38 +248,21 @@ class Request
);
}
// Dynamic route controller::handler argument builder
//
// Need to somehow re-work this ...
//
// I don't like the fact that the `$bytype` array is actually initializing the `db` and `user`!
// Should probably somehow use a `switch` statement for the `$bytype`
//
// NOTE: I cannot really make this process dynamic by searching the `container` array.
// So I can't do something like this: `if ($param->type === $container->type)` ...
// because for example; the `db` value is actually a closure because it's an `inline factory`
// It's not a `Twister\Db` ... yet! So I can't compare it to a `function (Db $db)` value!
// `Twister\Db !== Closure`
//
/**
* Dynamic route controller::handler argument builder
*/
private function _get_args_from_params(array $params)
{
/*
$byType = [ 'twister\container' => &$this->container,
'twister\db' => &$this->db,
// 'twister\user' => &$this->container->user,
'twister\request' => &$this
];
*/
$byType = $this->container->requestRouteParams;
$args = [];
foreach ($params as $param)
{
if ($param->hasType() && isset($byType[$type = strtolower($param->getType())]))
$args[] = $byType[$type];
else if (isset($_GET[$param->name]))
$args[] = $_GET[$param->name];
else if (isset($this->params[$param->name]))
$args[] = $this->params[$param->name];
else if (isset($_GET[$param->name]))
$args[] = $_GET[$param->name];
else if (isset($_POST[$param->name]))
$args[] = $_POST[$param->name];
else
@ -403,8 +333,6 @@ class Request @@ -403,8 +333,6 @@ class Request
}
// eg. request::build_url(array('scheme' => null)) == //host/path
// eg. request::build_url(array('scheme' => 'https', 'host' => 'example.com', 'path' => '/login')) == https://example.com/login
// eg. request::build_url(array('path' => '/login')) == http://host/login (NOTE: Any `default` (request::$query) query string will be ignored when `path` is specified!)

2
src/Response.php

@ -90,7 +90,7 @@ class Response @@ -90,7 +90,7 @@ class Response
if (isset($_SESSION))
session_write_close();
$this->container->db->close();
// $this->container->db->close();
}
/**

Loading…
Cancel
Save