diff --git a/classes/cache/cache_AbstractHandler.php b/classes/cache/cache_AbstractHandler.php
index f322d8b..5940ab3 100644
--- a/classes/cache/cache_AbstractHandler.php
+++ b/classes/cache/cache_AbstractHandler.php
@@ -17,6 +17,8 @@
*/
abstract class cache_AbstractHandler implements cache_HandlerInterface {
+ const DEFAULT_TTL = 0;
+
/**
* Weather or not cache system should throw exceptions
* @var bool
@@ -60,7 +62,10 @@ final public function get($key, $default = null, $namespace = 'default') {
* @param string $namespace The namespace under which the variable is stored [optional, default: 'default']
* @return boolen True on success or, False on failure
*/
- final public function set($key, $data, $ttl = 0, $namespace = 'default') {
+ final public function set($key, $data, $ttl = null, $namespace = 'default') {
+
+ if (is_null($ttl)) $ttl = static::DEFAULT_TTL;
+
try {
return $this->_set($key, $data, $ttl, $namespace);
} catch (Exception $e) {
diff --git a/classes/cache/cache_ApcHandler.php b/classes/cache/cache_ApcHandler.php
index 4745705..53c3c1f 100644
--- a/classes/cache/cache_ApcHandler.php
+++ b/classes/cache/cache_ApcHandler.php
@@ -17,6 +17,9 @@
*/
class cache_ApcHandler extends cache_AbstractHandler {
/* CONSTANTS */
+
+ const DEFAULT_TTL = 0;
+
/* PROPERTIES */
/* CONSTRUCTOR & DESTRUCTOR */
diff --git a/classes/cache/cache_HandlerInterface.php b/classes/cache/cache_HandlerInterface.php
index e4efc7f..acb03a8 100644
--- a/classes/cache/cache_HandlerInterface.php
+++ b/classes/cache/cache_HandlerInterface.php
@@ -18,6 +18,7 @@
*/
interface cache_HandlerInterface {
/* CONSTRUCTOR & DESTRUCTOR */
+
/* GET METHODS */
/**
diff --git a/classes/cache/cache_MemcacheHandler.php b/classes/cache/cache_MemcacheHandler.php
index d9b4dd5..18a33db 100644
--- a/classes/cache/cache_MemcacheHandler.php
+++ b/classes/cache/cache_MemcacheHandler.php
@@ -19,6 +19,8 @@
class cache_MemcacheHandler extends cache_AbstractHandler {
/* CONSTANTS */
+ const DEFAULT_TTL = 0;
+
/**
* Constant defining a minite storage duration
* @var integer
@@ -46,10 +48,17 @@ public function __construct($host, $port, $useExceptions = false) {
$this->useExceptions = $useExceptions;
$this->memcache = new Memcache;
+ if (is_null($host)) return;
+
if(!$this->memcache->connect($host, $port))
throw new cache_CouldNotConnectException('Could not connect to Memcache server');
}
-
+
+ // adds a memcached server to the connection pool
+ public function addServer ($host, $port = 11211) {
+ $this->memcache->addServer($host, $port);
+ }
+
/* GET METHODS */
/**
@@ -65,7 +74,7 @@ protected function _get($key, $default = null, $namespace = 'default') {
if(!is_array($return)) {
if($this->useExceptions)
- throw new cache_NotFoundException(sf('Could not fine \'%s\' variable', $key));
+ throw new cache_NotFoundException(sf('Could not find \'%s\' variable', $key));
$return = array($default);
}
diff --git a/classes/cache/cache_TemporaryStorageHandler.php b/classes/cache/cache_TemporaryStorageHandler.php
new file mode 100644
index 0000000..7ebd5da
--- /dev/null
+++ b/classes/cache/cache_TemporaryStorageHandler.php
@@ -0,0 +1,61 @@
+store[$namespace][$key]) &&
+ (is_null($this->store[$namespace][$key]['expires']) || $this->store[$namespace][$key]['expires'] > time()))
+ return $this->store[$namespace][$key]['value'];
+
+ else return $default;
+
+ }
+ protected function _set($key, $data, $ttl = null, $namespace = 'default') {
+
+ if (!isset($this->store[$namespace])) {
+ $this->store[$namespace] = array();
+ }
+
+ $this->store[$namespace][$key] = array(
+ 'value' => $data,
+ 'expires' => is_null($ttl)?null:(time() + $ttl)
+ );
+
+ }
+ protected function _delete($key, $namespace = 'default') {
+
+ if (isset($this->store[$namespace][$key]));
+ unset($this->store[$namespace][$key]);
+
+ }
+ protected function _exists($key, $namespace = 'default'){
+
+ return isset($this->store[$namespace][$key]) &&
+ (is_null($this->store[$namespace][$key]['expires']) || $this->store[$namespace][$key]['expires'] > time());
+ }
+
+ protected function _flush() {
+ $this->store = array();
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php
index 61b7752..53ef7a5 100644
--- a/classes/caster/caster_Abstract.php
+++ b/classes/caster/caster_Abstract.php
@@ -32,12 +32,17 @@ abstract class caster_Abstract {
/* MAGIC METHODS */
/* METHODS */
+ static public function getSpec() {
+ $caster = new static;
+ return $caster->spec;
+ }
+
/**
* Checks the given string for the number of caster characters.
* @param string $s The string to parse
*/
protected function numberOfPercents($s) {
- if(!is_string($s)) throw new caster_Exception('Parameter $s should be of type string');
+ if(!is_string($s)) throw new caster_Exception('Caster expecting format string, received '.gettype($s).': '.$s);
$pos = 0;
$count = 0;
while(true) {
@@ -79,6 +84,46 @@ public function castString($string, $args = null, $_ = null) {
}
return implode('', $ret);
}
+ /**
+ * Casts args into a given string/object by the class held caster spec
+ * @param string $string The string to cast
+ * @param mixed $args The args to be parsed into the string
+ * @return string The casted string
+ */
+ public function castObject($string, $args = null, $_ = null) {
+ // Get args
+ $args = func_get_args();
+ if (count($args[0]) !== 2) throw new caster_Exception('Cast object expects two parameters');
+
+ $ch = substr($args[0][0], 1);
+
+ if(array_key_exists($ch, $this->spec) && method_exists($this,$this->spec[$ch])) {
+ $methodName = $this->spec[$ch];
+ return $this->$methodName($args[0][1]);
+ } elseif($ch == '%') {
+ return '%';
+ } else {
+ throw new caster_Exception("Caster received unexpected format character: %".$ch);
+ }
+
+ }
+
+ public function castArray ($params) {
+ $format = array_shift ($params);
+ return $this->castReal ($format, $params);
+ }
+
+ public function castArraySets ($args) {
+ $ret = array ();
+ for ($i = 0; $i < count ($args); ) {
+ $format = $args [$i++];
+ $params = array_slice ($args, $i, $this->numberOfPercents($format));
+ $i += count ($params);
+ array_unshift ($params, $format);
+ $ret[] =$this->castArray ($params);
+ }
+ return $ret;
+ }
/**
* Casts args into a given string by the class held caster spec
@@ -108,10 +153,14 @@ protected function castReal($format, $args) {
if(array_key_exists($ch, $this->spec) && method_exists($this,$this->spec[$ch])) {
$methodName = $this->spec[$ch];
$ret .= $this->$methodName($args[$i++]);
+
+ } elseif(array_key_exists($ch, $this->spec) && method_exists($this,'cast_'.$this->spec[$ch])) {
+ $methodName = 'cast_'.$this->spec[$ch];
+ $ret .= $this->$methodName($args[$i++]);
} elseif($ch == '%') {
$ret .= '%';
} else {
- throw new caster_Exception("Invalid format string");
+ throw new caster_Exception("Caster received unexpected format character: %".$ch);
}
$pos0 = $pos1 + 2;
}
@@ -151,6 +200,14 @@ function formatNewLines($str) {
return $ret;
}
+ static function getCastName($format) {
+ $caster = new static;
+ return $caster->getCastNameReal($format);
+ }
+ function getCastNameReal ($format) {
+ return $this->spec[$format];
+ }
+
/* DEPRECATED METHODS */
}
diff --git a/classes/caster/caster_Json.php b/classes/caster/caster_Json.php
new file mode 100644
index 0000000..9426c4e
--- /dev/null
+++ b/classes/caster/caster_Json.php
@@ -0,0 +1,57 @@
+ 'string',
+ 'n' => 'number',
+
+ /* these aren't real JSON types, but can validate on them */
+ 'i' => 'integer',
+ 'f' => 'float',
+
+ 'b' => 'boolean',
+ 'a' => 'array',
+ 'o' => 'object',
+
+ 't' => 'timestamp'
+ );
+
+ /* annoyance due to PHP scope issue */
+ static function cast ($args) {
+ $parser = new self();
+ return (string) $parser->castString(func_get_args());
+ }
+
+ static function cast_string ($in) {
+ return sf("'%j'",$in);
+ }
+
+ static function cast_integer ($in) {
+ return intval($in);
+ }
+ static function cast_float ($in) {
+ return floatval($in);
+ }
+ static function cast_number ($in) {
+ return ($in);
+ }
+ static function cast_boolean ($in) {
+ return boolval($in);
+ }
+
+ static function cast_array ($in) {
+ return json_encode($in);
+ }
+ static function cast_timestamp ($in) {
+ return intval($in);
+ }
+
+ static function cast_object ($in) {
+ return json_encode($in);
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/caster/caster_Php.php b/classes/caster/caster_Php.php
new file mode 100644
index 0000000..bf1e0d3
--- /dev/null
+++ b/classes/caster/caster_Php.php
@@ -0,0 +1,63 @@
+ 'string',
+ 'n' => 'number',
+
+ /* these aren't real JSON types, but can validate on them */
+ 'i' => 'integer',
+ 'f' => 'float',
+
+ 'b' => 'boolean',
+
+ 't' => 'timestamp',
+
+ 'a' => 'array',
+ 'o' => 'object'
+ );
+
+
+ /* returns a value */
+ static function value ($format, $value) {
+
+ $parser = new self();
+
+ $methodName = 'cast_'.$parser->spec[$format];
+ return $parser->$methodName($value);
+
+ }
+
+ static function cast_string ($in) {
+ return strval($in);
+ }
+
+ static function cast_integer ($in) {
+ return intval($in);
+ }
+ static function cast_float ($in) {
+ return floatval($in);
+ }
+ static function cast_number ($in) {
+ return ($in);
+ }
+ static function cast_boolean ($in) {
+ return boolval($in);
+ }
+ static function cast_timestamp ($in) {
+ return intval($in);
+ }
+
+ static function cast_array ($in) {
+ return $in;
+ }
+
+ static function cast_object ($in) {
+ return $in;
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php
index d294521..260e78d 100644
--- a/classes/core/Atsumi.php
+++ b/classes/core/Atsumi.php
@@ -41,6 +41,14 @@ class Atsumi {
*/
private $errorHandler;
+
+ /**
+ * A data store
+ * @access private
+ * @var mvc_DynamicModel
+ */
+ private $store;
+
/* CONSTRUCTOR & DESTRUCTOR */
/**
@@ -53,7 +61,9 @@ private function __construct() {
$this->errorHandler = new atsumi_ErrorHandler();
// Load some helpful files
- atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'caster helpers/http');
+ \Atsumi\Core\Loader::references(\Atsumi\Core\Loader::getAtsumiDir(), 'caster utility/http');
+
+
}
/* GET METHODS */
@@ -103,6 +113,9 @@ public static function __callStatic($name, $arguments) {
if(preg_match('/^error__(.+)$/', $name, $matches))
return call_user_func_array(array($atsumi->errorHandler, $matches[1]), $arguments);
+ if(preg_match('/^store__(.+)$/', $name, $matches))
+ return call_user_func_array(array($atsumi->store, $matches[1]), $arguments);
+
if(method_exists($atsumi, '_'.$name))
return call_user_func_array(array($atsumi, '_'.$name), $arguments);
@@ -142,72 +155,16 @@ public function _initApp(atsumi_AbstractAppSettings $settings) {
$this->errorHandler->setDisplayErrors(false);
}
- $this->appHandler = new atsumi_AppHandler($settings, $this->errorHandler);
- }
-
- /*
- * NOTE: Below are all static functions which will be removed when php 5.3.0+ becomes the
- * common and the PHP magic function __callStatic works correctly
- */
- public static function start() {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function initApp($settings, $debug = false) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__setUriParser($parser) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__setPath($path) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__go($path) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__render() {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__getParserMetaData() {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function app__createUri($controller, $method) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function error__setRecoverer($recoverer) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function error__addObserver($observer, $eventType) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public static function error__removeObserver($observer) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
+
+ $this->appHandler = new atsumi_AppHandler($settings, $this->errorHandler);
+
}
- public static function error__setFloodControl(cache_HandlerInterface $cacheManager, $duration) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
+ public function _initStore () {
+ if (class_exists('mvc_DynamicModel'))
+ $this->store = new mvc_DynamicModel();
}
+
}
?>
\ No newline at end of file
diff --git a/classes/core/app/atsumi_AbstractAppSettings.php b/classes/core/app/atsumi_AbstractAppSettings.php
index 2c4b81c..80f037b 100644
--- a/classes/core/app/atsumi_AbstractAppSettings.php
+++ b/classes/core/app/atsumi_AbstractAppSettings.php
@@ -35,6 +35,7 @@ abstract class atsumi_AbstractAppSettings {
protected $cache = array();
/* CONSTRUCTOR & DESTRUCTOR */
+ public function __construct() { }
/* GET METHODS */
/**
@@ -134,4 +135,4 @@ protected function &initCache($name) {
return $this->cache[$name];
}
}
-?>
\ No newline at end of file
+?>
diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php
index 288f0da..eaa7814 100644
--- a/classes/core/app/atsumi_AppHandler.php
+++ b/classes/core/app/atsumi_AppHandler.php
@@ -44,6 +44,13 @@ class atsumi_AppHandler {
*/
private $uri;
+ /**
+ * The command to be parsed and processed
+ * @access private
+ * @var string
+ */
+ private $command;
+
/**
* Holds the base path of atsumi relative to the domain
* @access private
@@ -58,6 +65,13 @@ class atsumi_AppHandler {
*/
private $uriParser = 'uriparser_Gyokuro';
+ /**
+ * The command parser classname or instance to use
+ * @access private
+ * @var string|object
+ */
+ private $claParser = 'claparser_Standard';
+
/**
* The controller instance generated based on the parser data
* @access private
@@ -97,6 +111,15 @@ public function __construct($settings, $errorHandler) {
/* GET METHODS */
+ /**
+ * Returns the parser return data
+ * @access public
+ * @return array|null The parser return data, or null on error
+ */
+ public function getParserData() {
+ return $this->parserData;
+ }
+
/**
* Returns the parser return data
* @access public
@@ -106,8 +129,31 @@ public function getParserMetaData() {
return $this->parserMetaData;
}
+ /**
+ * get the base path of atsumi relative to the domain for if atsumi is running in a subdirectory
+ * @access public
+ * @param string $basePath
+ */
+ public function getBaseUri() {
+ return $this->baseUri;
+ }
+
+ public function getController() {
+ return $this->controller;
+ }
+
// SET FUNCTIONS
+ /**
+ * Sets the command to be parsed and processed
+ * @access public
+ * @param string $path
+ */
+ public function setCommand($command) {
+ $this->command = $command;
+ }
+
+
/**
* Sets the uri to be parsed and processed
* @access public
@@ -166,13 +212,18 @@ public function createUri($controller, $method) {
* @param string $uri The uri to processed
*/
public function go($uri) {
-
- $scriptArr = explode('/',$_SERVER['SCRIPT_NAME']);
- $baseUri = str_replace(array($_SERVER['DOCUMENT_ROOT'], end($scriptArr)), '', $_SERVER['SCRIPT_FILENAME']);
- $this->setBaseUri($baseUri);
-
- $this->setUri($uri);
- $this->parseUri();
+ if(isset($this->settings->get_cli) && $this->settings->get_cli === true) {
+ $this->setBaseUri('.');
+ $this->setCommand($uri);
+ $this->parseCommand();
+ } else {
+ $scriptArr = explode('/',$_SERVER['SCRIPT_NAME']);
+ $baseUri = str_replace(array($_SERVER['DOCUMENT_ROOT'], end($scriptArr)), '', $_SERVER['SCRIPT_FILENAME']);
+ $this->setBaseUri($baseUri);
+
+ $this->setUri($uri);
+ $this->parseUri();
+ }
$this->process();
}
@@ -181,7 +232,7 @@ public function go($uri) {
* @access public
*/
public function parseUri() {
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:parse:uri');
if(!in_array('uriparser_Interface', class_implements($this->uriParser)))
throw new Exception('URI Parser must implement uriparser_Interface');
@@ -202,7 +253,34 @@ public function parseUri() {
atsumi_Debug::record('Uri Parsing',
'Uri was parsed to determine the controller, method and args.',
- array_merge(array('path' => $this->uri), $this->parserData), true);
+ array_merge(array('path' => $this->uri), $this->parserData),
+ 'app:parse:uri'
+ );
+ }
+
+ public function parseCommand() {
+ atsumi_Debug::startTimer('app:parse:command');
+ if(!in_array('claparser_Interface', class_implements($this->claParser))) {
+ throw new Exception('Command Line Argument parser must implement claparser_Interface');
+ }
+ if(is_string($this->claParser)) {
+ $this->claParser = new $this->claParser();
+ }
+ $parseData = $this->claParser->parseCommand($this->command, $this->settings->init_specification);
+
+ $this->parserData = array(
+ 'controller' => $parseData['controller'],
+ 'method' => $parseData['method'],
+ 'args' => $parseData['args']
+ );
+
+ atsumi_Debug::setParserData($parseData);
+ atsumi_Debug::record('Command Parsing',
+ 'Command was parsed to determine the controller, method and args.',
+ array_merge(array('path' => $this->command), $this->parserData),
+ 'app:parse:command'
+ );
+
}
/**
@@ -211,12 +289,12 @@ public function parseUri() {
* @access public
*/
public function process() {
+
// Could possibly be a fragment of the spec
if(!is_string($this->parserData['controller']))
- throw new Exception('Path parsing error, please report to developement team');
-
+ throw new app_PageNotFoundException('Path parsing error, please report to developement team');
if(!class_exists($this->parserData['controller']))
- throw new Exception('Could not find required controller: '.$this->parserData['controller']);
+ throw new app_PageNotFoundException('Could not find required controller: '.$this->parserData['controller']);
$classname = $this->parserData['controller'];
$this->controller = new $classname($this->settings, $this->errorHandler);
@@ -225,28 +303,28 @@ public function process() {
throw new app_PageNotFoundException();
// Get the debugger and start a timer for processing
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:processing');
// Add the method to the list of processed methods
$this->controller->addProcessedMethod($this->parserData['method'], $this->parserData['args']);
// Time and execute the pre process
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:preProcess');
$this->controller->preProcess();
- atsumi_Debug::record('Controller PreProcess', 'Before the controllers method was called the pre-process function was executed', null, true);
+ atsumi_Debug::record('Controller PreProcess', 'Before the controllers method was called the pre-process function was executed', null, 'app:controller:preProcess');
// Time and execute the controllers method
- atsumi_Debug::startTimer();
- call_user_func_array(array($this->controller, $this->parserData['method']), $this->parserData['args']);
- atsumi_Debug::record('Controller Method', 'The controllers requested method was executed', null, true);
+ atsumi_Debug::startTimer('app:controller:method');
+ $this->controller->processRequest($this->parserData['method'], $this->parserData['args']);
+ atsumi_Debug::record('Controller Method', 'The controllers requested method was executed', null, 'app:controller:method');
// Time and execute the post process
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:postProcess');
$this->controller->postProcess();
- atsumi_Debug::record('Controller PostProcess', 'After the controllers method was called the post-process function was executed', null, true);
+ atsumi_Debug::record('Controller PostProcess', 'After the controllers method was called the post-process function was executed', null, 'app:controller:postProcess');
// Log the whole processing time
- atsumi_Debug::record('Controller Processing Compleate', 'All processing was compleated successfully', null, true);
+ atsumi_Debug::record('Controller Processing Complete', 'All processing was completed successfully', null, 'app:controller:processing');
}
/**
@@ -257,9 +335,10 @@ public function process() {
public function render() {
// Time and execute the pre render
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:preRender');
+ $this->controller->publishFlashData();
$this->controller->preRender();
- atsumi_Debug::record('Controller PreRender', 'Before rendering was processed the pre-render function was executed', null, true);
+ atsumi_Debug::record('Controller PreRender', 'Before rendering was processed the pre-render function was executed', null, 'app:controller:preRender');
$viewHandler = $this->controller->getViewHandler();
$view = $this->controller->getView();
@@ -276,25 +355,48 @@ public function render() {
// Get the debugger and start a timer for rendering
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:rendering');
- $this->controller->publishFlashData();
$viewData = $this->controller->getViewData();
atsumi_Debug::setViewData($viewData);
// Time and execute the view handler
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:render');
$viewHandler->render($view, $viewData);
- atsumi_Debug::record('Rendering', sf('Rendering was performed by the %s view handler', get_class($viewHandler)), null, true);
+ atsumi_Debug::record('Rendering', sf('Rendering was performed by the %s view handler', get_class($viewHandler)), null, 'app:controller:render');
+
+
+ // tell the debug to print if required as connection will be closed by __destruct
+ atsumi_Debug::printIfRequired();
+
// Time and execute the post render
- atsumi_Debug::startTimer();
+ atsumi_Debug::startTimer('app:controller:postRender');
$this->controller->postRender();
- atsumi_Debug::record('Controller PostRender', 'After the rendering was processed the post-render function was executed', null, true);
+ atsumi_Debug::record('Controller PostRender', 'After the rendering was processed the post-render function was executed', null, 'app:controller:postRender');
// Log the whole processing time
- atsumi_Debug::record('Rendering Compleate', 'All rendering was compleated successfully', null, true);
+ atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, 'app:controller:rendering');
+ }
+
+ public function dispatchConnection () {
+
+ flush();
+ ob_flush();
+ if (session_id()) session_write_close();
+
+ fastcgi_finish_request();
+ }
+
+ public function postConnectionProcess () {
+
+ // page_name turns in to after_name
+ $afterMethod = 'after_'.substr($this->parserData['method'],5);
+
+ if (method_exists($this->controller, $afterMethod))
+ $this->controller->processRequest($afterMethod, $this->parserData['args']);
+
}
}
-?>
\ No newline at end of file
+?>
diff --git a/classes/core/app/exceptions/app_InvalidUsageException.php b/classes/core/app/exceptions/app_InvalidUsageException.php
new file mode 100644
index 0000000..14c0fce
--- /dev/null
+++ b/classes/core/app/exceptions/app_InvalidUsageException.php
@@ -0,0 +1,37 @@
+
diff --git a/classes/core/app/parser/claparser_Interface.php b/classes/core/app/parser/claparser_Interface.php
new file mode 100644
index 0000000..f9028ff
--- /dev/null
+++ b/classes/core/app/parser/claparser_Interface.php
@@ -0,0 +1,23 @@
+
diff --git a/classes/core/app/parser/claparser_Standard.php b/classes/core/app/parser/claparser_Standard.php
new file mode 100644
index 0000000..52f034d
--- /dev/null
+++ b/classes/core/app/parser/claparser_Standard.php
@@ -0,0 +1,19 @@
+
diff --git a/classes/core/app/parser/uriparser_Gyokuro.php b/classes/core/app/parser/uriparser_Gyokuro.php
index 612cc09..22cdb20 100644
--- a/classes/core/app/parser/uriparser_Gyokuro.php
+++ b/classes/core/app/parser/uriparser_Gyokuro.php
@@ -29,7 +29,8 @@ static function processArg ($arg) {
// Numeric handling. 1e2 is not handled as numeric as can cause issues
// with slices of hashes being unintentionally parsed a numeric
- if (is_numeric($arg) && !strpos(strtolower($arg), 'e')) {
+ // does't process as numeric if leads with a 0
+ if (is_numeric($arg) && !strpos(strtolower($arg), 'e') && !(strlen($arg) > 1 && substr($arg, 0,1) === '0')) {
// check if is float
if (strpos($arg, '.')) {
@@ -121,9 +122,9 @@ public static function searchSpec($specification, $controller, $pathArr = array(
*/
public function createUri($specification, $controller, $method, $args = array()) {
$components = self::searchSpec($specification, $controller);
-
// TODO: add args parser!!
- $path = count($components) ? implode('/',$components).'/'. $method.'/':'';
+ $path = (count($components) ? implode('/',$components).'/':''). ($method ==''?'':$method.'/');
+
return $path;
}
diff --git a/classes/core/app/parser/uriparser_Matcha.php b/classes/core/app/parser/uriparser_Matcha.php
new file mode 100644
index 0000000..9490682
--- /dev/null
+++ b/classes/core/app/parser/uriparser_Matcha.php
@@ -0,0 +1,80 @@
+array('gb'),
+ 'fr'=>array()
+ )
+ )
+ );
+
+ This would accept:
+
+ - en
+ - en-gb
+ - fr
+
+ there is a locale array available in the meta
+ data that contains language & country
+
+*/
+
+class uriparser_Matcha extends uriparser_Gyokuro {
+
+
+ private $locale = null;
+ private $supportedLanguages = array();
+
+ // you can optionally pass in the supported languages array
+ // this will prevent matching none supported 2 letter pages
+ public function __construct ($supportedLanguages = array()) {
+ $this->supportedLanguages = $supportedLanguages;
+ $this->locale = new mvc_LocaleModel();
+ }
+
+
+ public function parseUri($uri, $specification) {
+
+
+ preg_match('@^/([a-z]{2})(-([a-z]{2})){0,1}/@', $uri, $localeMatch, PREG_OFFSET_CAPTURE);
+
+ if (array_key_exists(0,$localeMatch) && (
+
+ // check that it's in the allowed languages
+ !count($this->supportedLanguages) ||
+ in_array(
+ $localeMatch[1][0],
+ array_keys($this->supportedLanguages)
+ )
+
+ )) {
+
+ $this->locale->set('language', $localeMatch[1][0]);
+
+ if (array_key_exists(3,$localeMatch) && (
+
+ // check it's an allowed country for current language
+ !count($this->supportedLanguages) ||
+ in_array(
+ $localeMatch[3][0],
+ $this->supportedLanguages[$this->locale->get('language')]
+ )
+ )) {
+ $this->locale->set('country', $localeMatch[3][0]);
+ }
+
+ $uri = substr($uri, strlen($localeMatch[0][0])-1);
+ }
+
+ $output = parent::parseUri($uri, $specification);
+ $output['meta']['locale'] = $this->locale;
+
+ return $output;
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php
old mode 100644
new mode 100755
index 1df4985..22717f3
--- a/classes/core/debug/atsumi_Debug.php
+++ b/classes/core/debug/atsumi_Debug.php
@@ -99,13 +99,20 @@ class atsumi_Debug {
* @var array
*/
protected $timers = array();
+ protected $timerMap = array();
/**
- * Weather the debugger is active and recording information
+ * Is the debugger active and recording information
* @var boolean
*/
protected $active = false;
+ /**
+ * If this is set to true then it will record data regardless of active
+ * @var boolean
+ */
+ protected $record = false;
+
/**
* A path to a log file used to output debug data
* @var string
@@ -144,12 +151,23 @@ private function __construct() {}
* @access public
*/
public function __destruct() {
+ self::printIfRequired();
+ }
+
+ static function getConsoleData() {
+ $d = self::getInstance();
+ return $d->_getConsoleData();
+ }
+
+ static function printIfRequired() {
+
+ $d = self::getInstance();
try {
- if($this->active && $this->autoRender)
- echo $this->render();
+ if($d->active && $d->autoRender)
+ echo $d->render();
} catch (Exception $e) { }
}
-
+
/* GET FUNCTIONS */
/**
@@ -167,24 +185,28 @@ protected static function getInstance() {
}
/**
- * Returns weather or not the debugger is actively recording data
+ * Returns debugger active
* @access public
* @return boolean
*/
public function _getActive() {
- return $active;
+ return $this->active;
}
/* SET FUNCTIONS */
/**
- * Sets weather or not the debugger is actively recording data
+ * Sets if debugger is actively recording data
* @access public
* @param boolean $val If the debugger should be recording [optional, default: true]
*/
public function _setActive($val = true) {
$this->active = $val;
}
+
+ public function _setRecord($val = true) {
+ $this->record = $val;
+ }
/**
* Sets weather or not the debugger should automaticlly render on destruction
@@ -302,8 +324,11 @@ public function _addDatabase( /*db_InterfaceDatabase */ $database) {
* Adds a timer point onto a stack of timers
* @access public
*/
- public function _startTimer() {
- $this->timers[] = microtime(true);
+ public function _startTimer($key = null) {
+ if (!is_null($key))
+ $this->timerMap[$key] = microtime(true);
+ else
+ $this->timers[] = microtime(true);
}
/**
@@ -311,11 +336,20 @@ public function _startTimer() {
* @access public
* @return string
*/
- public function _endTimer() {
+ public function _endTimer($key = null) {
+
+ if (!is_null($key)) {
+ if (!isset($this->timerMap[$key])) return '??';
+ $ms = round((microtime(true) - $this->timerMap[$key]), 3);
+ unset($this->timerMap[$key]);
+ return $ms;
+ }
+
$startTime = array_pop($this->timers);
return round((microtime(true) - $startTime), 3).' microseconds';
}
+
/**
* Records an event along with optional data, timer and area the data belongs to
* Note: This will do nothing if the debugger is not active
@@ -327,14 +361,26 @@ public function _endTimer() {
* @param string $area The area to add the data to
*/
public function _record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) {
- if(!$this->active) return;
+ if(!$this->active && !$this->record) return;
+
+ if ($timer === true)
+ $time = self::_endTimer();
+ else if (is_string($timer)) {
+ $time = self::_endTimer($timer);
+ }
+ else $time = 0;
+
+ $timerString = '(Process Time: '.$time.')';
+
+
$this->consoleData[] = array(
'title' => $title,
'desc' => $desc,
'data' => $data,
'area' => $area,
- 'timestamp' =>($timer ? '(Process Time: '.self::_endTimer().')' : '')
+ 'time' => $time,
+ 'timestamp' => $timerString
);
}
@@ -359,6 +405,10 @@ public function _addArea($name, $color) {
$this->areas[$name] = $color;
}
+ public function _getConsoleData() {
+ return $this->consoleData;
+ }
+
/**
* Formats a variable into a html5 valid string representation
* @access protected
@@ -366,6 +416,7 @@ public function _addArea($name, $color) {
* @return string A html5 valid string representation of the variable
*/
protected function format($value) {
+
if(is_null($value))
return 'NULL';
@@ -387,12 +438,24 @@ protected function format($value) {
if(is_array($value)) {
$ret = 'Array(';
foreach($value as $key => $item)
- $ret .= sf('
[%s] => %s
', $key, $this->format($item));
+ $ret .= sf('[%s] => %s
', $key, stripos($key,'password')?'*****':$this->format($item));
$ret .= ')';
return $ret;
}
- if(is_object($value)) {
+ if(is_object($value) && $value instanceof mvc_AbstractModel) {
+
+ $objOutput = '';
+ $objArray = $value->getStructure();
+ foreach($objArray as $key => $item)
+ $objOutput .= sf('[%s] => %s
', $key, stripos($key,'password')?'*****':$this->format($value->get($key)));
+
+
+ return sf('(Model) %s:%s', get_class($value), $objOutput);
+
+ } elseif(is_object($value)) {
+ return sf('(Object) %s', get_class($value));
+ /*
if(method_exists($value,'toString'))
return $value->toString();
@@ -405,6 +468,7 @@ protected function format($value) {
$ret .= sf('[%s] => %s
', $key, $this->format($item));
$ret .= ')';
return $ret;
+ */
}
return sf('(%s)%s', gettype($value), $value);
}
@@ -646,8 +710,8 @@ protected function returnCss() {
.debugConsole .typeUnknown {color: orange; }
.logTitle {font-size: 14px; font-weight: bold;}
.logTimestamp {font-size: 10px; font-weight: normal; color: #6677DD;}
- .logDesc {font-size: 10px; font-style: italic; color:#777; margin-top:0.5em; }
- .logData { background-color:#ddd; border:1px solid #ccc; padding:1em; margin-top:1em; }
+ .logDesc {font-size: 10px; font-style: italic; color:#777; margin-top:0.5em; white-space:pre-line; }
+ .logData { background-color:#ddd; border:1px solid #ccc; padding:1em; margin-top:1em; white-space:pre-line; }
.debugDragToggle {height:5px; border-left:3px double #555; border-right:3px double #555; width:2px; margin:0 auto 0 auto; font-size:0px; display:block; }
.debugFooter { background-color:#eee; border-top:2px inset #ccc; height:20px; padding:5px; text-align:right; color:#777; font-size: 12px; }
.debugFooter span { color:#777; }
@@ -664,9 +728,9 @@ protected function returnCss() {
* @return string The debugger as a valid HTML5 string
*/
public function _render() {
-
if(!$this->active) return;
+
$this->startTimer();
$display =(isset($_COOKIE['debugDisplay']) ? strtolower($_COOKIE['debugDisplay']) : 'console');
ob_start();
@@ -700,13 +764,19 @@ public function _render() {
- foreach($this->consoleData as $data) : ?>
-
+
+consoleData as $data) :
+ $color = dechex(crc32($data['area']));
+ $color = '#'.substr($color, 0, 6);
+
+ ?>
+
+
=$data['title'];?> =$data['timestamp'];?>
=$data['desc'];?>
=(!is_null($data['data']) ? sf(' %s ', $this->format($data['data'])) : '');?>
- endforeach; ?>
+
@@ -768,16 +838,17 @@ public function _render() {
-
+databases as $key => $database) :
$totalTime = 0;
- foreach($database->getQueryTimes() as $query)
- $totalTime += $query['time']
+ foreach($database->getQueryTimes() as $query) {
+ $totalTime += $query['time'];
+ }
?>
- Database =$key;?>
- Total Query time: =$totalTime;?>
+ Database #=$key;?>
+ x=count($database->getQueryTimes())?> queries. Time: =$totalTime;?> ms
=str_replace(array('\t', '\n'), array(" "," "), $this->format($database->getQueryTimes()));?>
- endforeach; ?>
+
@@ -825,6 +896,12 @@ public static function setViewData($data) {
$args = func_get_args();
self::__callStatic(__FUNCTION__, $args);
}
+
+ public static function setRecord($data) {
+ $args = func_get_args();
+ self::__callStatic(__FUNCTION__, $args);
+ }
+
public static function setParserData($data) {
$args = func_get_args();
@@ -846,12 +923,12 @@ public static function addDatabase($database) {
self::__callStatic(__FUNCTION__, $args);
}
- public static function startTimer() {
- self::__callStatic(__FUNCTION__, array());
+ public static function startTimer($key = null) {
+ self::__callStatic(__FUNCTION__, func_get_args());
}
- public static function endTimer() {
- return self::__callStatic(__FUNCTION__, array());
+ public static function endTimer($key = null) {
+ return self::__callStatic(__FUNCTION__, func_get_args());
}
public static function record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) {
@@ -867,5 +944,22 @@ public static function addArea($name, $color) {
public static function render() {
return self::__callStatic(__FUNCTION__, array());
}
+
+ public static function getDbQueries() {
+ return self::__callStatic(__FUNCTION__, array());
+ }
+
+ public function _getDbQueries () {
+
+ $out = array();
+ foreach($this->databases as $key => $database) {
+ $db = array();
+ foreach($database->getQueryTimes() as $query) {
+ $db[] = $query;
+ }
+ $out[] = $db;
+ }
+ return $out;
+ }
}
?>
\ No newline at end of file
diff --git a/classes/core/error/atsumi_ErrorEventArgs.php b/classes/core/error/atsumi_ErrorEventArgs.php
index a3e94ea..40a7ff3 100644
--- a/classes/core/error/atsumi_ErrorEventArgs.php
+++ b/classes/core/error/atsumi_ErrorEventArgs.php
@@ -37,7 +37,7 @@ class atsumi_ErrorEventArgs extends atsumi_EventArgs {
* @param Exception $e The exception that was thrown
* @param string $recover The recover that was used
*/
- public function __construct(Exception $e, $recoverer = '') {
+ public function __construct(Exception $e, $recoverer = null) {
$this->exception = $e;
$this->recoverer = $recoverer;
}
diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php
index d1bfd80..fff1ffc 100644
--- a/classes/core/error/atsumi_ErrorHandler.php
+++ b/classes/core/error/atsumi_ErrorHandler.php
@@ -139,7 +139,7 @@ private function blockedByFloodControl($type, $line, $file) {
if($this->cacheHandler->get($key, false, 'errorHandler'))
return true;
else return false;
- } catch(cache_NotInCacheException $e) {
+ } catch(cache_NotFoundException $e) {
return false;
}
}
@@ -182,17 +182,26 @@ public function handleError($errNumber, $errString, $errFile, $errLine, $errCont
* @access public
* @param Exception $e The exception that was thrown
*/
- public function handleException(Exception $e) {
+ public function handleException(Exception $e, $recover = true) {
try {
// fires the exception_fc event if not blocked by flood control
if(!$this->blockedByFloodControl(get_class($e), $e->getLine(), $e->getFile()))
- $this->fireEvent(self::EVENT_EXCEPTION_FC, new atsumi_ErrorEventArgs($e, &$this->recoverer));
+ $this->fireEvent(
+ self::EVENT_EXCEPTION_FC,
+ new atsumi_ErrorEventArgs($e, $recover?$this->recoverer:null)
+ );
// fire the exception event regardless of flood control
- $this->fireEvent(self::EVENT_EXCEPTION, new atsumi_ErrorEventArgs($e, &$this->recoverer));
-
- $this->recoverer->recover($e);
+ $this->fireEvent(
+ self::EVENT_EXCEPTION,
+ new atsumi_ErrorEventArgs($e, $recover?$this->recoverer:null)
+ );
+
+ if ($recover)
+ $this->recoverer->recover($e);
+ else
+ return;
} catch(Exception $e) {
exit(__CLASS__.' Error: '.$e->getMessage()."\n".$e->getFile().' #'.$e->getLine(). PHP_EOL.$e->getTraceAsString());
}
@@ -216,13 +225,30 @@ protected function fireEvent($eventType, atsumi_EventArgs $args = null) {
// remove failed listener
$this->removeObserver($listener);
// handle the exception
- $this->handleException(new errorHandler_Exception(sf("Listener '%s' failed: ", get_class($listener), $listenerException->getMessage())));
+ $this->handleException(
+ new errorHandler_Exception(
+ sf("Listener '%s' failed: ",
+ get_class($listener),
+ $listenerException->getMessage()
+ )
+ )
+ );
}
}
// record exception as listened to
if ($eventType == self::EVENT_EXCEPTION_FC)
- $this->recordInFloodControl(get_class($args->exception), $args->exception->getLine(), $args->exception->getFile());
+ $this->recordInFloodControl(
+ get_class($args->exception),
+ $args->exception->getLine(),
+ $args->exception->getFile()
+ );
+ }
+ public function listen ($e) {
+ $this->handleException($e, false);
+ }
+ public function recover ($e) {
+ $this->recoverer->recover($e);
}
}
?>
\ No newline at end of file
diff --git a/classes/core/error/exceptions/exception_Duplicate.php b/classes/core/error/exceptions/exception_Duplicate.php
new file mode 100644
index 0000000..1d4bd0a
--- /dev/null
+++ b/classes/core/error/exceptions/exception_Duplicate.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/classes/core/error/exceptions/exception_NotFound.php b/classes/core/error/exceptions/exception_NotFound.php
new file mode 100644
index 0000000..50264e4
--- /dev/null
+++ b/classes/core/error/exceptions/exception_NotFound.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/classes/core/error/listeners/listener_AddToSession.php b/classes/core/error/listeners/listener_AddToSession.php
index 591e417..71c90ca 100644
--- a/classes/core/error/listeners/listener_AddToSession.php
+++ b/classes/core/error/listeners/listener_AddToSession.php
@@ -45,6 +45,7 @@ class listener_AddToSession implements atsumi_Observer {
private $namespace;
private $method;
+ private $onlyWhenRecovering;
/* CONSTRUCTOR & DESTRUCTOR */
@@ -55,12 +56,13 @@ class listener_AddToSession implements atsumi_Observer {
* @param mixed $data The data to add when the error occurs
* @param string $namespace The namespace to add under if atsumi session is being used [optional, default: session_Handler::DEFAULT_NAMESPACE]
*/
- public function __construct($key, $data, $namespace = session_Handler::DEFAULT_NAMESPACE, $method= self::METHOD_SET) {
+ public function __construct($key, $data, $namespace = session_Handler::DEFAULT_NAMESPACE, $method= self::METHOD_SET, $onlyWhenRecovering = false) {
$this->data = $data;
$this->key = $key;
$this->namespace = $namespace;
$this->method = $method;
+ $this->onlyWhenRecovering = $onlyWhenRecovering;
}
/* GET METHODS */
@@ -96,6 +98,7 @@ protected function mergeDataToSession($errorText) {
* @param atsumi_EventArgs $args Any args related to the event
*/
public function notify(atsumi_Observable $sender, atsumi_EventArgs $args) {
+ if ($this->onlyWhenRecovering && !$args->recoverer) return;
$this->mergeDataToSession(atsumi_ErrorParser::parse($args->exception, atsumi_ErrorParser::PLAINTEXT, $args->recoverer));
}
}
diff --git a/classes/core/error/listeners/listener_LogToFile.php b/classes/core/error/listeners/listener_LogToFile.php
index 1785cfd..4447966 100644
--- a/classes/core/error/listeners/listener_LogToFile.php
+++ b/classes/core/error/listeners/listener_LogToFile.php
@@ -26,6 +26,7 @@ class listener_LogToFile implements atsumi_Observer {
* @var string
*/
private $logDir;
+ private $filePrefix;
/* CONSTRUCTOR & DESTRUCTOR */
@@ -34,8 +35,9 @@ class listener_LogToFile implements atsumi_Observer {
* @access public
* @param string $logDir The directory to place the log files in
*/
- public function __construct($logDir) {
+ public function __construct($logDir, $filePrefix = '') {
$this->logDir = $logDir;
+ $this->filePrefix = $filePrefix;
}
/* GET METHODS */
@@ -49,13 +51,14 @@ public function __construct($logDir) {
* @param string $dataIn The data to write to the error log
*/
protected function writeToLog($dataIn) {
- $filename = date('Y-m-d').'.log';
- $handle = @fopen($this->logDir.$filename, 'a');
+ $filename = $this->filePrefix.($this->filePrefix?'-':'').date('Y-m-d').'.log';
+ $filePath = $this->logDir.DIRECTORY_SEPARATOR.$filename;
+ $handle = @fopen($filePath, 'a');
if(!$handle)
- throw new errorHandler_ListenerException('Cannot open log file: '.$this->logDir.$filename);
+ throw new errorHandler_ListenerException('Cannot open log file: '.$filePath);
$write = fwrite($handle, $dataIn);
- if ($write === false) throw new errorHandler_ListenerException('Cannot no write log file: '.$this->logDir.$filename);
+ if ($write === false) throw new errorHandler_ListenerException('Cannot no write log file: '.$filePath);
fclose($handle);
}
diff --git a/classes/core/error/listeners/listener_NewRelic.php b/classes/core/error/listeners/listener_NewRelic.php
new file mode 100644
index 0000000..e94e1e4
--- /dev/null
+++ b/classes/core/error/listeners/listener_NewRelic.php
@@ -0,0 +1,13 @@
+exception);
+ }
+
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/core/error/listeners/listener_Sentry.php b/classes/core/error/listeners/listener_Sentry.php
new file mode 100644
index 0000000..f94395b
--- /dev/null
+++ b/classes/core/error/listeners/listener_Sentry.php
@@ -0,0 +1,29 @@
+dsn = $dsn;
+ $this->release = $release;
+ $this->tags = $tags;
+ }
+
+ public function notify(atsumi_Observable $sender, atsumi_EventArgs $args) {
+
+ $client = new Raven_Client(
+ $this->dsn,
+ array (
+ 'release' => $this->release,
+ 'tags' => $this->tags
+ )
+ );
+
+ $client->captureException($args->exception);
+
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/core/error/parser/atsumi_ErrorParser.php b/classes/core/error/parser/atsumi_ErrorParser.php
index 6b42268..5821a6e 100644
--- a/classes/core/error/parser/atsumi_ErrorParser.php
+++ b/classes/core/error/parser/atsumi_ErrorParser.php
@@ -65,7 +65,7 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer
case self::PLAINTEXT:
$out .= sfl('###### ATSUMI has caught an Exception : %s', date(DATE_ATOM));
$out .= sfl("\n".' >> %s <<', $e->getMessage());
- $out .= sfl("\n".'Exception type: '.get_class($e));
+ $out .= sfl("\n".'Exception type: %s',get_class($e));
if($e instanceof ErrorException)
$out .= sfl("\n".'Severity level: '.$e->getSeverity());
@@ -85,11 +85,18 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer
$out .= sfl('Recoverer action: %s', $recoverer->getActionDetails());
}
+ if (isset($e->details) && !is_null($e->details)) {
+ $out .= sfl("\n".'-Additional Detail');
+ $out .= sfl('%s', pretty($e->details));
+ }
+
+
if($e instanceof atsumi_AbstractException) {
$out .= sfl("\n".'-How to resolve this issue');
$out .= sfl($e->getInstructions('text/plain'));
}
+
$out .= sfl("\n".'-Stack Trace');
$out .= sfl('%s', atsumi_ErrorParser::formatTrace($e->getTrace()));
$out .= sfl('###### End of Exception '."\n\n");
@@ -118,6 +125,11 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer
$out .= sfl(' Recoverer action: %s', $recoverer->getActionDetails());
}
+ if (isset($e->details) && !is_null($e->details)) {
+ $out .= sfl(' Additional Detail');
+ $out .= sfl(' ', pretty($e->details));
+ }
+
if($e instanceof atsumi_AbstractException) {
$out .= sfl(' ATSUMI: How to resolve this issue');
$out .= sfl(' %s ', $e->getInstructions('text/html'));
diff --git a/classes/core/loader/Loader.php b/classes/core/loader/Loader.php
new file mode 100644
index 0000000..38d4b6e
--- /dev/null
+++ b/classes/core/loader/Loader.php
@@ -0,0 +1,413 @@
+workspace = $this->findWorkspace();
+ }
+
+ // GET FUNCTIONS
+
+ /**
+ * Creates and/or returns a singlton instance of the class
+ * Note: Protected prevents anything externally calling this function. Use static functions.
+ * @access protected
+ * @return Loader A singlton instance of the loader
+ */
+ protected static function getInstance() {
+ static $sInstance;
+
+ if(!is_object($sInstance))
+ $sInstance = new self;
+
+ return $sInstance;
+ }
+
+ /**
+ * Gets the name of the atsumi directory, incase it has been name differently by the developer
+ * @access public
+ * @return string The atsumi directory name
+ */
+ private function _getAtsumiDir() {
+ return $this->atsumiDir;
+ }
+
+ /**
+ * Gets the workspace of atsumi and the all related projects
+ * @access public
+ * @return string A absolute path to the workspace
+ */
+ private function _getWorkspace() {
+ return $this->workspace;
+ }
+
+ /* MAGIC METHODS */
+
+ /**
+ * PHP Magic function, used to call methods on singleton staticly
+ * @access public
+ * @param string $name The name of the method being called
+ * @param array $arguments The arguments being passed to the method
+ * @return mixed The result of the function call
+ */
+ public static function __callStatic($name, $arguments) {
+ $instance = self::getInstance();
+
+ if(method_exists($instance, '_'.$name))
+ return call_user_func_array(array($instance, '_'.$name), $arguments);
+
+ // USe sprintf. No guarantee of sf at this point.
+ throw new \Exception(sprintf('Undefined call to %s::%s', get_class($instance), $name));
+ }
+
+ /* METHODS */
+
+ /**
+ * Return an absolute path to the base directory containing atsumi and developer projects
+ * which is used as the base folder for loading external project files from
+ * @access protected
+ * @return string An absolute path to the project base directory
+ */
+ protected function findWorkspace() {
+ $matches = null;
+
+ $file = str_replace('\\', '/', __FILE__);
+
+ if(!preg_match('|^(.*)/classes/core/loader/Loader.php$|', $file, $matches))
+ throw new \Exception('The atsumi loader cannot find its workspace!');
+
+ $matches = explode('/', $matches[1]);
+ $this->atsumiDir = array_pop($matches);
+ return implode('/', $matches);
+ }
+
+
+ /**
+ * Used to load or include a set of files as specified in the spec
+ *
+ * Examples:
+ * references('atsumi', 'utility mvc database');
+ * references('atsumi', array('utility', 'mvc', 'database'));
+ * references(array('atsumi' => 'utility mvc database'));
+ * @access public
+ * @param $spec mixed See above for examples
+ */
+ public function _references($spec) {
+
+
+ $args = func_get_args();
+ switch(func_num_args()) {
+ case 1:
+ if(!is_array($args[0]))
+ throw new \Exception('Loader spec must be an associative array');
+ $spec = $args[0];
+ break;
+ case 2:
+ if(is_string($args[0]) && is_string($args[1])) {
+ $spec = array($args[0] => $args[1]);
+ } else {
+ $this->projectNamespace = $args[0];
+ $spec = $args[1];
+ }
+ break;
+ default:
+ throw new \Exception('Loader: Invalid number of args');
+ break;
+ }
+ $domains = '';
+ foreach($spec as $domain => $parts) {
+
+ if(isset($this->processedPaths[$domain.':'.$parts])) continue;
+
+ $this->processedPaths[$domain.':'.$parts] = true;
+
+ if(!is_array($parts)) {
+ $parts = explode(' ', $parts);
+ }
+
+ foreach($parts as $part) {
+ if (empty($part)) continue;
+ $success = false;
+ if($this->useRequire($domain, $part)) $success = true;
+ if($this->useDir($domain, $part, 'src')) $success = true;
+ if($this->useDir($domain, $part, 'classes')) $success = true;
+
+ if(!$success) throw new \loader_ClassNotFoundException(
+ sprintf('Unknown reference in Loader::references() : %s:%s', $domain, $part), $domain);
+ }
+ }
+
+ }
+
+ /**
+ * Tries to find a class instance within the classes directory and add it to the autoloader
+ * @access protected
+ * @param string $domain The project domain folder to look in
+ * @param string $part The subfolder to process
+ * @return boolean If the part was found
+ */
+ protected function useDir($domain, $part, $dirName) {
+ $path = sprintf('%s/%s/%s/%s', $this->workspace, $domain, $dirName, $part);
+ if(!is_dir($path)) return false;
+ self::useClassDir($path, $domain);
+ return true;
+ }
+
+ /**
+ * Tries to find an include a required file in the includes directory
+ * @access protected
+ * @param string $domain The project domain folder to look in
+ * @param string $part The name of the file to look for
+ * @return boolean If the part was found
+ */
+ protected function useRequire($domain, $part) {
+ $path = sprintf('%s/%s/include/%s.php', $this->workspace, $domain, $part);
+ if(!is_file($path)) return false;
+
+ require_once($path);
+ return true;
+ }
+
+ /**
+ * Process a directory presuming all files are named relative to the class they contain
+ * @access protected
+ * @param string $path The absolute path to the directory to process
+ */
+ protected function useClassDir($path, $domain) {
+ $contents = scandir($path);
+ foreach($contents as $fileName) {
+ if(substr($fileName, 0, 1) == '.') continue;
+
+ $fullPath = $path.'/'.$fileName;
+
+ // Recursive loading of subdirectories
+ if(is_dir($fullPath)) {
+ self::useClassDir($fullPath, $domain);
+ continue;
+ }
+ if(is_file($fullPath) && preg_match('/^([a-zA-Z_][a-zA-Z0-9_]*)[\.inc\|]*.php$/', $fileName, $matches)) {
+ $className = str_replace('.php', '', $matches[0]);
+ $className = str_replace('.inc', '', $className);
+ self::_registerClass($className, $fullPath);
+ self::_registerNamespace($className, $fullPath, $domain);
+ }
+ }
+ }
+
+ /**
+ * Used to register a class to the auto loader
+ * @access public
+ * @param string $classname The name of the class
+ * @param string $filePath An absolute path to the file containing the class
+ */
+ public function _registerClass($classname, $filePath) {
+ $classname = strtolower($classname);
+
+ /** DEPRECATED FOR NAMESPACES
+ if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) {
+ throw new Loader\Exceptions\DuplicateClassException(
+ 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath
+ );
+ }**/
+
+ $this->classes[$classname] = $filePath;
+ }
+
+ /**
+ * Used to register a namespace to the auto loader
+ * @access public
+ * @param string $classname The name of the class
+ * @param string $filePath An absolute path to the file containing the class
+ */
+ public function _registerNamespace($classname, $filePath, $domain) {
+
+ // get rid of folders
+ if (strpos($domain, '/')) {
+ $parts = explode('/', $domain);
+ $domain = array_pop($parts);
+ $folder = implode('/',$parts);
+ } else {
+ $folder = '';
+ }
+
+ // Strip out the base dir
+ $namespacePath = str_replace($this->_getWorkspace(), '', $filePath);
+ $namespacePath = str_replace($folder, '', $namespacePath);
+
+ // Get the dir levels as array
+ $dirStruct = explode('/', trim($namespacePath, '/'));
+
+
+ // Work out if we're in Atsumi
+ $baseNamespace = array_shift($dirStruct);
+ if(trim($baseNamespace, '/') == trim($this->_getAtsumiDir(), '/')) {
+ $baseNamespace = $this->atsumiNamespace;
+ } else if($this->projectNamespace) {
+ $baseNamespace = $this->projectNamespace;
+ }
+
+ $baseNamespace = '\\' . $baseNamespace;
+
+
+ $namespace = '';
+ $i = 0;
+ foreach($dirStruct as $dir) {
+
+
+ // Ignore the standard classes dir
+ if($dir == 'classes' || $dir == 'src') {
+ continue;
+ }
+
+ // if project name is same as first dir then assume namespace
+ if (++$i == 1 && strtolower($baseNamespace) == '\\' . strtolower($dir)) {
+ $baseNamespace = '\\' . ucfirst($dir);
+ continue;
+ }
+ $namespace .= '\\' . ucfirst($dir);
+ }
+
+ $namespace = $baseNamespace . $namespace;
+
+ // Finally, trim the file extension
+ $namespace = substr($namespace, 0, strrpos($namespace, '.'));
+
+ if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) {
+ throw new \loader_DuplicateClassException(
+ 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath
+ );
+ }
+
+ $this->namespaces[trim($namespace, '\\')] = $filePath;
+ }
+
+ /**
+ * Used by the PHP auto load function to load a class
+ * @access public
+ * @param string $classname The name of the class to load
+ */
+ public function _loadClass($classname) {
+ if(strpos($classname, '\\') !== false) {
+ return $this->_loadClassByNamespace($classname);
+ } else {
+ return $this->_loadClassByName($classname);
+ }
+ }
+
+ private function _loadClassByNamespace($namespace) {
+ if(!array_key_exists(trim($namespace, '\\'), $this->namespaces)) {
+ return;
+ }
+ require_once($this->namespaces[$namespace]);
+ }
+
+ private function _loadClassByName($classname) {
+ $classname = strtolower($classname);
+
+ if(!array_key_exists($classname, $this->classes))
+ return;
+
+ //throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname);
+
+ require_once($this->classes[$classname]);
+ }
+
+ static public function loadClass($className) {
+ $args = func_get_args();
+ return self::__callStatic(__FUNCTION__, $args);
+ }
+
+}
+
+/**
+ * register the autoloader
+ */
+spl_autoload_register(array('Atsumi\Core\Loader', 'loadClass'));
+
+?>
\ No newline at end of file
diff --git a/classes/core/loader/atsumi_Loader.php b/classes/core/loader/atsumi_Loader.php
index 0ce3111..750b4f9 100644
--- a/classes/core/loader/atsumi_Loader.php
+++ b/classes/core/loader/atsumi_Loader.php
@@ -1,308 +1,15 @@
workspace = $this->findWorkspace();
- }
-
- // GET FUNCTIONS
-
- /**
- * Creates and/or returns a singlton instance of the class
- * Note: Protected prevents anything externally calling this function. Use static functions.
- * @access protected
- * @return atsumi_Loader A singlton instance of the loader
- */
- protected static function getInstance() {
- static $sInstance;
-
- if(!is_object($sInstance))
- $sInstance = new self;
-
- return $sInstance;
- }
-
- /**
- * Gets the name of the atsumi directory, incase it has been name differently by the developer
- * @access public
- * @return string The atsumi directory name
- */
- public function _getAtsumiDir() {
- return $this->atsumiDir;
- }
-
- /**
- * Gets the workspace of atsumi and the all related projects
- * @access public
- * @return string A absolute path to the workspace
- */
- public function _getWorkspace() {
- return $this->workspace;
- }
-
- /* MAGIC METHODS */
-
- /**
- * PHP Magic function, used to call methods on singleton staticly
- * @access public
- * @param string $name The name of the method being called
- * @param array $arguments The arguments being passed to the method
- * @return mixed The result of the function call
- */
- public static function __callStatic($name, $arguments) {
- $instance = self::getInstance();
-
- if(method_exists($instance, '_'.$name))
- return call_user_func_array(array($instance, '_'.$name), $arguments);
-
- throw new Exception('Undefined call to atsumi_Debug::'.$name);
- }
-
- /* METHODS */
-
- /**
- * Return an absolute path to the base directory containing atsumi and developer projects
- * which is used as the base folder for loading external project files from
- * @access protected
- * @return string An absolute path to the project base directory
- */
- protected function findWorkspace() {
- $matches = null;
-
- $file = str_replace('\\', '/', __FILE__);
-
- if(!preg_match('|^(.*)/classes/core/loader/atsumi_Loader.php$|', $file, $matches))
- throw new Exception('The atsumi loader cannot find its workspace!');
-
- $matches = explode('/', $matches[1]);
- $this->atsumiDir = array_pop($matches);
- return implode('/', $matches);
- }
-
- /**
- * Used to load or include a set of files as specified in the spec
- *
- * Examples:
- * references('atsumi', 'helpers mvc database');
- * references('atsumi', array('helpers', 'mvc', 'database'));
- * references(array('atsumi' => 'helpers mvc database'));
- * @access public
- * @param $spec mixed See above for examples
- */
- public function _references($spec) {
- $args = func_get_args();
- switch(func_num_args()) {
- case 1:
- if(!is_array($args[0]))
- throw new Exception('Loader spec must be an associative array');
- $spec = $args[0];
- break;
- case 2:
- $spec = array($args[0] => $args[1]);
- break;
- default:
- throw new Exception('atsumi_Loader: Invalid number of args');
- break;
- }
-
- $domains = '';
- $collections = '';
- foreach($spec as $domain => $parts) {
- $domains .= $domain.', ';
- if(isset($this->processedPaths[$domain.':'.$parts])) continue;
- $this->processedPaths[$domain.':'.$parts] = true;
-
- if(!is_array($parts)) {
- $collections .= '['.$parts.']';
- $parts = explode(' ', $parts);
- } else {
- $collections .= '['.implode(', ', $parts).']';
- }
-
- foreach($parts as $part) {
- if (empty($part)) continue;
- $success = false;
- if($this->useRequire($domain, $part)) $success = true;
- if($this->useClasses($domain, $part)) $success = true;
-
- if(!$success) throw new Exception(sprintf('Unknown reference in atsumi_Loader::references() : %s:%s', $domain, $part));
- }
- }
- }
-
- /**
- * Tries to find a class instance within the classes directory and add it to the autoloader
- * @access protected
- * @param string $domain The project domain folder to look in
- * @param string $part The subfolder to process
- * @return boolean If the part was found
- */
- protected function useClasses($domain, $part) {
- $path = sprintf('%s/%s/classes/%s', $this->workspace, $domain, $part);
- if(!is_dir($path)) return false;
- self::useClassDir($path);
- return true;
- }
-
- /**
- * Tries to find an include a required file in the includes directory
- * @access protected
- * @param string $domain The project domain folder to look in
- * @param string $part The name of the file to look for
- * @return boolean If the part was found
- */
- protected function useRequire($domain, $part) {
- $path = sprintf('%s/%s/include/%s.php', $this->workspace, $domain, $part);
- if(!is_file($path)) return false;
-
- require_once($path);
- return true;
- }
-
- /**
- * Process a directory presuming all files are named relative to the class they contain
- * @access protected
- * @param string $path The absolute path to the directory to process
- */
- protected function useClassDir($path) {
- $contents = scandir($path);
- foreach($contents as $fileName) {
- if(substr($fileName, 0, 1) == '.') continue;
-
- $fullPath = $path.'/'.$fileName;
-
- // Recursive loading of subdirectories
- if(is_dir($fullPath)) {
- self::useClassDir($fullPath);
- continue;
- }
- if(is_file($fullPath) && preg_match('/^([a-zA-Z_][a-zA-Z0-9_]*)[\.inc\|]*.php$/', $fileName, $matches)) {
- $className = str_replace('.php', '', $matches[0]);
- $className = str_replace('.inc', '', $className);
- self::_registerClass($className, $fullPath);
- }
- }
- }
-
- /**
- * Used to register a class to the auto loader
- * @access public
- * @param string $classname The name of the class
- * @param string $filePath An absolute path to the file containing the class
- */
- public function _registerClass($classname, $filePath) {
- $classname = strtolower($classname);
-
- if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) {
- throw new loader_DuplicateClassException(
- 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath
- );
- }
-
- $this->classes[$classname] = $filePath;
- }
-
- /**
- * Used by the PHP auto load function to load a class
- * @access public
- * @param string $classname The name of the class to load
- */
- public function _loadClass($classname) {
- $classname = strtolower($classname);
- if(!array_key_exists($classname, $this->classes))
- throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname);
-
- require_once($this->classes[$classname]);
- }
-
- /*
- * NOTE: Below are all static functions which will be removed when php 5.3.0+ becomes the
- * common and the PHP magic function __callStatic works correctly
- */
- public static function getAtsumiDir() {
- return self::__callStatic(__FUNCTION__, array());
- }
-
- public static function getWorkspace() {
- return self::__callStatic(__FUNCTION__, array());
+ static public function references ($args) {
+ return \Atsumi\Core\Loader::references ($args);
}
-
- public static function references($spec) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- public function registerClass($className, $filePath) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
- }
-
- static public function loadClass($className) {
- $args = func_get_args();
- return self::__callStatic(__FUNCTION__, $args);
+ static public function getWorkspace () {
+ return \Atsumi\Core\Loader::getWorkspace ();
}
}
-/**
- * Used internally by PHP to help attempt to load missing classes
- * @param $className The class to load
- */
-function __autoload($classname) {
- atsumi_Loader::loadClass($classname);
-}
+
?>
\ No newline at end of file
diff --git a/classes/database/adapter/mysql/caster_MySql.php b/classes/database/adapter/mysql/caster_MySql.php
new file mode 100644
index 0000000..1c7ae18
--- /dev/null
+++ b/classes/database/adapter/mysql/caster_MySql.php
@@ -0,0 +1,229 @@
+ 'tableName',
+ 'b' => 'boolean',
+ 'c' => 'character',
+ 'C' => 'characterVarying',
+ 'd' => 'datetime',
+ 'D' => 'date',
+ 'f' => 'float',
+ 'i' => 'integer',
+ 'I' => 'integerOrNull',
+ 'l' => 'literal',
+ 'n' => 'numeric',
+ 's' => 'text',
+ 'S' => 'textOrNull',
+ 't' => 'timestamp',
+ 'T' => 'time',
+ 'x' => 'binary',
+ 'z' => 'interval',
+ 'Z' => 'intervalOrNull'
+ );
+
+ /**
+ * Casts a string in a MySql format
+ * NOTE: Annoyance due to PHP scope issue
+ * @param string $string The string to cast
+ * @param mixed $args The args to be parsed into the string
+ * @param mixed $_ Repeated last arg as needed
+ * @return string The casted string
+ */
+ static function cast($string, $args = null, $_ = null) {
+ $parser = new self();
+
+ /* 'func_get_args' cannot be called as function arg pre PHP5 */
+ $func_args = func_get_args();
+ return (string)$parser->castString($func_args);
+ }
+
+ /**
+ * Casts a variable into a MySql table name
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function tableName($in) {
+ return sf('%s', $in);
+ }
+
+ /**
+ * Casts a variable into a MySql text
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function text($in) {
+ if(!is_string($in)) throw new caster_StrictTypeException('Expected String');
+ return sf("CAST('%s' as CHAR)", mysql_escape_string($in));
+ }
+
+ /**
+ * Casts a variable into a MySql text thats accepts NULL values
+ * @param string $in String to be casted or null
+ * @return string Casted string
+ */
+ static function textOrNull($in) {
+ if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null');
+
+ if (is_string($in) && strlen($in)) return self::text($in);
+ elseif (is_null($in)) return 'NULL';
+ }
+
+ /**
+ * Casts a variable into a MySql character
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function character($in) {
+ return self::text($in);
+ }
+
+ /**
+ * Casts a variable into a MySql character varying
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function characterVarying($in) {
+ return self::text($in);
+ }
+
+ /**
+ * Casts a variable into a MySql float
+ * @param float $in Float to be casted
+ * @return string Casted string
+ */
+ static function float($in) {
+ return sf("CAST('%s' as DECIMAL)", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql boolean
+ * @param bool $in Bool to be casted
+ * @return string Casted string
+ */
+ static function boolean($in) {
+ return sf("%s", $in ? 'true':'false');
+ }
+
+ /**
+ * Casts a variable into a MySql integer
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function integer($in) {
+ return sf("CAST('%s' as SIGNED)", intval($in));
+ }
+ /**
+ * Casts a variable into a MySql integer or Null
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function integerOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return sf("CAST('%s' as SIGNED)", intval($in));
+ }
+
+ /**
+ * Casts a variable into a MySql numeric
+ * @param mixed $in Mixed to be casted
+ * @return string Casted string
+ */
+ static function numeric($in) {
+ return sf("CAST('%s' as SIGNED)", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql binary
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function binary($in) {
+ return sf("CAST('%s' as BINARY)", pg_escape_bytea($in));
+ }
+
+ /**
+ * Casts a variable into a MySql timestamp with timezone
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function timestamp($in) {
+ //TODO: convert datetime to timestamp or leave to user input
+ //return sf("'%s'::TIMESTAMP WITH TIME ZONE", mysql_escape_string(gmdate('Y-m-d H:i:s+00', $in)));
+ return sf("'%s'", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql datetime
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function datetime($in) {
+ return sf("CAST('%s' as DATETIME)", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql date
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function date($in) {
+ return sf("CAST('%s' as DATE)", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql time
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function time($in) {
+ return sf("CAST('%s' as TIME)", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql date
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function interval($in) {
+ //TODO: interval?
+ return sf("'%s'", $in);
+ }
+ static function intervalOrNull($in) {
+ //TODO: interval or null?
+ if (is_null($in)) return 'NULL';
+ return sf("'%s'", $in);
+ }
+
+ /**
+ * Performs no casting on the variable, leaving it as it is
+ * @param mixed $in mixed to be casted
+ * @return string Unaffected string
+ */
+ static function literal($in) {
+ return $in;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/classes/database/adapter/mysql/caster_MySqlToPhp.php b/classes/database/adapter/mysql/caster_MySqlToPhp.php
new file mode 100644
index 0000000..bdc4456
--- /dev/null
+++ b/classes/database/adapter/mysql/caster_MySqlToPhp.php
@@ -0,0 +1,147 @@
+ 'boolean',
+ 'd' => 'datetime',
+ 'D' => 'datetimeOrNull',
+ 'e' => 'date', //TODO: decide on letter for date in mysql
+ 'E' => 'dateOrNull',
+ 'i' => 'integer',
+ 'I' => 'integerOrNull',
+ 'f' => 'float',
+ 'F' => 'floatOrNull',
+ 's' => 'text',
+ 'S' => 'textOrNull',
+ 't' => 'timestamp',
+ 'T' => 'timestampOrNull',
+ 'u' => 'time', //TODO: decide in letter for time in mysql
+ 'U' => 'timeOrNull'
+ );
+
+ /**
+ * Casts a string in a MySql format
+ * NOTE: Annoyance due to PHP scope issue
+ * @param string $string The string to cast
+ * @param mixed $args The args to be parsed into the string
+ * @param mixed $_ Repeated last arg as needed
+ * @return string The casted string
+ */
+ static function cast($string, $args = null, $_ = null) {
+ $parser = new self();
+
+ /* 'func_get_args' cannot be called as function arg pre PHP5 */
+ $func_args = func_get_args();
+ return $parser->castObject($func_args);
+ }
+
+
+ /**
+ * Casts a variable into a MySql text
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function text($in) {
+ if(!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')');
+ return sf("%s", $in);
+ }
+
+ /**
+ * Casts a variable into a MySql boolean
+ * @param bool $in Bool to be casted
+ * @return string Casted string
+ */
+ static function boolean($in) {
+ if ($in==='0') $in = false;
+ if ($in==='1') $in = true;
+ if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')');
+ return $in;
+ }
+
+ /**
+ * Casts a variable into a MySql integer
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function integer($in) {
+ if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')');
+ return intval($in);
+ }
+
+ static function integerOrNull($in) {
+ if (is_null($in)) return null;
+ if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')');
+ return intval($in);
+ }
+
+ static function float($in) {
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')');
+ setType($in, 'float');
+ return $in;
+ }
+
+ static function floatOrNull($in) {
+ if (is_null($in)) return null;
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float or Null, received: '.$in.' ('.gettype($in).')');
+ setType($in, 'float');
+ return $in;
+ }
+
+ static function datetime($in) {
+ #return atsumi_Date::fromYmd($in);
+ return new atsumi_DateTime(strtotime($in));
+ }
+
+ static function datetimeOrNull($in) {
+ if (is_null($in)) return null;
+ return new atsumi_DateTime(strtotime($in));
+ }
+
+ static function timestamp($in) {
+ return new atsumi_DateTime(strtotime($in));
+ }
+
+ static function timestampOrNull($in) {
+ if (is_null($in)) return null;
+ return self::timestamp($in);
+ }
+
+ static function date($in) {
+ return atsumi_Date::fromYmd($in);
+ }
+
+ static function time($in) {
+ if (is_null($in)) return null;
+ //TODO: time stuff
+ return $in;
+ }
+
+ static function timeOrNull($in) {
+ //TODO: time (or null) stuff
+ if (is_null($in)) return null;
+ return $in;
+ }
+}
+?>
diff --git a/classes/database/adapter/mysql/db_MySql.php b/classes/database/adapter/mysql/db_MySql.php
new file mode 100755
index 0000000..74314e1
--- /dev/null
+++ b/classes/database/adapter/mysql/db_MySql.php
@@ -0,0 +1,115 @@
+connectReal($conString, $config);
+ }
+
+ protected function createRow ($rowData) {
+ return new db_MySqlRow($rowData);
+ }
+
+ /**
+ * Initalise the vender type caster
+ */
+ protected function initCaster() {
+ $this->caster = new caster_MySql();
+ }
+
+ /**
+ * Returns true if the database supports transaction
+ * @return boolean If the database supports transactions
+ */
+ public function transactionSupport() {
+ // MySam does't support transactions and will just ignore begin, commit, rollback, etc
+ // Innodb will work as expected
+ return true;
+ }
+
+ /**
+ * Returns true if the database is in transaction
+ * @return boolean If the database is in transaction
+ */
+ public function inTransaction() {
+ return $this->transaction;
+ }
+
+ /**
+ * Begins a database transaction. Will fail if there is already a transaction in progress.
+ * @return boolean If beginning the transaction was successful
+ */
+ public function transactionBegin() {
+ if($this->transaction)
+ throw new db_Exception('Cannot call beginTransaction() while already in transaction');
+
+ $this->query('BEGIN');
+ $this->transaction = true;
+ return true;
+ }
+
+ /**
+ * Commits a database transaction. Will fail if there is no transaction in progress.
+ * @return boolean If commiting the transaction was successful
+ */
+ public function transactionCommit() {
+ if(!$this->transaction)
+ throw new db_Exception('Cannot call commitTransaction() while not in transaction');
+
+ $this->transaction = false;
+ return $this->query('COMMIT');
+ }
+
+ /**
+ * Rolls back a database transaction. Will fail if there is no transaction in progress.
+ * @return boolean If rolling back the transaction was successful
+ */
+ public function transactionRollback() {
+ if(!$this->transaction)
+ throw new db_Exception('Cannot call rollbackTransaction() While not in transaction');
+
+ $this->transaction = false;
+ return $this->query('ROLLBACK');
+ }
+
+ /**
+ * Rolls back a database transaction, if there is one in progress. This is designed for use in
+ * catch blocks to ensure a rollback in case of error.
+ */
+ public function transactionAutoRollback() {
+ if($this->transaction)
+ $this->transactionRollback();
+ }
+
+ public function nextval ($name) {
+ //TODO: return last id + 1?
+ return false;
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/database/adapter/mysql/db_MySqlRow.php b/classes/database/adapter/mysql/db_MySqlRow.php
new file mode 100644
index 0000000..69b7576
--- /dev/null
+++ b/classes/database/adapter/mysql/db_MySqlRow.php
@@ -0,0 +1,12 @@
+caster))
+ $this->caster = new caster_MySqlToPhp();
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/database/vender/postgresql/caster_PostgreSQL.php b/classes/database/adapter/postgresql/caster_PostgreSql.php
similarity index 72%
rename from classes/database/vender/postgresql/caster_PostgreSQL.php
rename to classes/database/adapter/postgresql/caster_PostgreSql.php
index 3626e34..b3b00b9 100644
--- a/classes/database/vender/postgresql/caster_PostgreSQL.php
+++ b/classes/database/adapter/postgresql/caster_PostgreSql.php
@@ -16,7 +16,7 @@
* @subpackage Caster
* @since 1.0
*/
-class caster_PostgreSQL extends caster_Abstract {
+class caster_PostgreSql extends caster_Abstract {
/* CONSTANTS */
/* PROPERTIES */
@@ -27,20 +27,28 @@ class caster_PostgreSQL extends caster_Abstract {
protected $spec = array(
'@' => 'tableName',
'a' => 'sqlArray',
+ 'A' => 'sqlArrayOrNull',
'b' => 'boolean',
'c' => 'character',
'C' => 'characterVarying',
'd' => 'date',
+ 'e' => 'bigInteger',
+ 'E' => 'bigIntegerOrNull',
'f' => 'float',
+ 'g' => 'geometry',
'i' => 'integer',
+ 'I' => 'integerOrNull',
'l' => 'literal',
'n' => 'numeric',
+ 'N' => 'numericOrNull',
'q' => 'fullTextQuery',
's' => 'text',
'S' => 'textOrNull',
't' => 'timestampWithTimezone',
'v' => 'fullTextVector',
'x' => 'binary',
+ 'z' => 'interval',
+ 'Z' => 'intervalOrNull'
);
/* CONSTRUCTOR & DESTRUCTOR */
@@ -105,11 +113,15 @@ static function sqlArray($in) {
$sqlArr = "";
foreach($in as $item) {
$sqlArr .= ($sqlArr == "") ? "" : ", ";
- $sqlArr .= is_int($item) ? $item : "'".$item."'";
+ $sqlArr .= is_int($item) ? $item : "'".pg_escape_string($item)."'";
}
return "ARRAY[".$sqlArr."]";
}
+ static function sqlArrayOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return self::sqlArray($in);
+ }
/**
* Casts a variable into a PostgreSQL character
@@ -144,7 +156,17 @@ static function float($in) {
* @return string Casted string
*/
static function boolean($in) {
- return sf("%s::BOOLEAN", $in?'t':'f');
+ return sf("'%s'::BOOLEAN", $in?'t':'f');
+ }
+
+ /**
+ * Casts a variable into a PostgreSQL geometry
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function geometry($in) {
+ if(!is_string($in)) throw new caster_StrictTypeException('Geom Expected String');
+ return sf("'%s'::GEOMETRY", pg_escape_string($in));
}
/**
@@ -153,9 +175,36 @@ static function boolean($in) {
* @return string Casted string
*/
static function integer($in) {
- return sf("%s::INTEGER", $in);
+ return sf("%s::INTEGER", intval($in));
+ }
+ /**
+ * Casts a variable into a PostgreSQL integer or Null
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function integerOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return sf("%s::INTEGER", intval($in));
}
+ /**
+ * Casts a variable into a PostgreSQL bigint
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function bigInteger($in) {
+ return sf("%s::BIGINT", intval($in));
+ }
+ /**
+ * Casts a variable into a PostgreSQL big int or Null
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function bigIntegerOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return sf("%s::BIGINT", intval($in));
+ }
+
/**
* Casts a variable into a PostgreSQL numeric
* @param mixed $in Mixed to be casted
@@ -164,6 +213,15 @@ static function integer($in) {
static function numeric($in) {
return sf("%s::NUMERIC", $in);
}
+ /**
+ * Casts a variable into a PostgreSQL numeric or Null
+ * @param int $in Mixed to be casted
+ * @return string Casted string
+ */
+ static function numericOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return sf("%s::NUMERIC", $in);
+ }
/**
* Casts a variable into a PostgreSQL binary
@@ -192,6 +250,19 @@ static function date($in) {
return sf("'%s'::DATE", $in);
}
+ /**
+ * Casts a variable into a PostgreSQL date
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function interval($in) {
+ return sf("'%s'::INTERVAL", $in);
+ }
+ static function intervalOrNull($in) {
+ if (is_null($in)) return 'NULL';
+ return sf("'%s'::INTERVAL", $in);
+ }
+
/**
* Performs no casting on the variable, leaving it as it is
* @param mixed $in mixed to be casted
diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php
new file mode 100644
index 0000000..05204ed
--- /dev/null
+++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php
@@ -0,0 +1,246 @@
+ 'sqlArrayOrNull',
+ 'a' => 'sqlArray',
+ 'b' => 'boolean',
+ 'B' => 'booleanOrNull',
+ 'd' => 'date',
+ 'D' => 'dateOrNull',
+ 'e' => 'integer',
+ 'E' => 'integerOrNull',
+ 'g' => 'geometry',
+ 'G' => 'geometryOrNull',
+ 'i' => 'integer',
+ 'I' => 'integerOrNull',
+ 'n' => 'numeric',
+ 'N' => 'numericOrNull',
+ 'f' => 'float',
+ 'F' => 'floatOrNull',
+ 's' => 'text',
+ 'S' => 'textOrNull',
+ 't' => 'timestampWithTimezone',
+ 'T' => 'timestampWithTimezoneOrNull',
+ 'z' => 'interval',
+ 'Z' => 'intervalOrNull'
+ );
+
+ /* CONSTRUCTOR & DESTRUCTOR */
+ /* GET METHODS */
+ /* SET METHODS */
+ /* MAGIC METHODS */
+ /* METHODS */
+
+ /**
+ * Casts a string in a PostgreSQL format
+ * NOTE: Annoyance due to PHP scope issue
+ * @param string $string The string to cast
+ * @param mixed $args The args to be parsed into the string
+ * @param mixed $_ Repeated last arg as needed
+ * @return string The casted string
+ */
+ static function cast($string, $args = null, $_ = null) {
+ $parser = new self();
+
+ /* 'func_get_args' cannot be called as function arg pre PHP5 */
+ $func_args = func_get_args();
+ return $parser->castObject($func_args);
+ }
+
+
+ /**
+ * Casts a variable into a PostgreSQL text
+ * @param string $in String to be casted
+ * @return string Casted string
+ */
+ static function text($in) {
+ if(!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')');
+ return sf("%s", $in);
+ }
+
+ /**
+ * Casts a variable into a PostgreSQL text thats accepts NULL values
+ * @param string $in String to be casted or null
+ * @return string Casted string
+ */
+ static function textOrNull($in) {
+ if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null, received: '.$in.' ('.gettype($in).')');
+
+ if (is_string($in) && strlen($in)) return self::text($in);
+ elseif (is_null($in)) return null;
+ }
+
+ /**
+ * Casts a variable into a PostgreSQL array
+ * @param array $in Array to be casted
+ * @return string Casted string
+ */
+ static function sqlArray($in) {
+
+ if (!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')');
+ if (is_null($in)) return array();
+
+ $in = str_replace(array("{","}"),"", $in);
+
+ $arrayOut = array();
+ $processing= $in;
+
+ while(strlen($processing) > 0) {
+
+ if (substr($processing, 0, 1) == ',') {
+ $processing = substr($processing, 1);
+ }
+
+ preg_match('/^\"([^"]*)\"/', $processing, $match);
+ if (count($match)) {
+
+ if (strlen($processing) === strlen($match[0]))
+ $processing = '';
+ else $processing = substr($processing, strlen($match[0]));
+
+ $arrayOut[] = $match[1];
+ continue;
+ }
+
+ preg_match('/^([^,]*)/', $processing, $match);
+ if (count($match)) {
+
+ if (strlen($processing) === strlen($match[0]))
+ $processing = '';
+ else $processing = substr($processing, strlen($match[0]));
+
+ $arrayOut[] = $match[1];
+ continue;
+ }
+ }
+ // int
+ /* if ($type == "integer") {
+ $newArr = array();
+ foreach ($arr as $val)
+ $newArr[] = intval($val);
+ $arr = $newArr;
+ } */
+ return $arrayOut;
+ }
+
+ static function sqlArrayOrNull($in) {
+ if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null, received: '.$in.' ('.gettype($in).')');
+
+ if (is_string($in)) return self::sqlArray($in);
+ elseif (is_null($in)) return null;
+ }
+
+ /**
+ * Casts a variable into a PostgreSQL boolean
+ * @param bool $in Bool to be casted
+ * @return string Casted string
+ */
+ static function boolean($in) {
+
+ if (is_string($in) && $in == 'f') $in = false;
+ if (is_string($in) && $in == 't') $in = true;
+ if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')');
+ return $in;
+ }
+ static function booleanOrNull($in) {
+ if (is_null($in)) return null;
+ return self::boolean($in);
+ }
+
+ static function interval($in) {
+ return atsumi_Interval::fromPostgresql(strval($in));
+ }
+ static function intervalOrNull($in) {
+ if (is_null($in)) return null;
+ return self::interval($in);
+ }
+
+ /**
+ * Casts a variable into a PostgreSQL integer
+ * @param int $in Int to be casted
+ * @return string Casted string
+ */
+ static function integer($in) {
+ if (is_string($in)) $in = intval($in);
+ if (!is_int($in)) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')');
+ return intval($in);
+ }
+ static function integerOrNull($in) {
+ if (is_null($in)) return null;
+ if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')');
+ return intval($in);
+ }
+
+ static function numeric($in) {
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Numeric, received: '.$in.' ('.gettype($in).')');
+ return $in;
+ }
+ static function numericOrNull($in) {
+ if (is_null($in)) return null;
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Numeric, received: '.$in.' ('.gettype($in).')');
+ return $in;
+ }
+
+
+ static function float($in) {
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')');
+ setType($in, 'float');
+ return $in;
+ }
+ static function floatOrNull($in) {
+ if (is_null($in)) return null;
+ if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float or Null, received: '.$in.' ('.gettype($in).')');
+ setType($in, 'float');
+ return $in;
+ }
+
+ static function date($in) {
+ return atsumi_Date::fromYmd($in);
+ }
+ static function dateOrNull($in) {
+ if (is_null($in)) return null;
+ return atsumi_Date::fromYmd($in);
+ }
+
+ static function timestampWithTimezone($in) {
+ return new atsumi_DateTime(strtotime($in));
+ }
+ static function timestampWithTimezoneOrNull($in) {
+ if (is_null($in)) return null;
+ return self::timestampWithTimezone($in);
+ }
+
+ static function geometry($in) {
+ return self::text($in);
+ }
+ static function geometryOrNull($in) {
+ return self::textOrNull($in);
+ }
+
+ /* DEPRECATED METHODS */
+}
+?>
\ No newline at end of file
diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/adapter/postgresql/db_PostgreSql.php
similarity index 90%
rename from classes/database/vender/postgresql/db_PostgreSQL.php
rename to classes/database/adapter/postgresql/db_PostgreSql.php
index 95fa269..8459dba 100644
--- a/classes/database/vender/postgresql/db_PostgreSQL.php
+++ b/classes/database/adapter/postgresql/db_PostgreSql.php
@@ -42,11 +42,16 @@ public function connect($config = array()) {
$this->connectReal($conString, $config);
}
+ protected function createRow ($rowData) {
+ return new db_PostgreSQLRow($rowData);
+ }
+
+
/**
* Initalise the vender type caster
*/
protected function initCaster() {
- $this->parser = new caster_PostgreSQL();
+ $this->caster = new caster_PostgreSql();
}
/**
@@ -61,7 +66,7 @@ public function transactionSupport() {
* Returns true if the database is in transaction
* @return boolean If the database is in transaction
*/
- public function transaction() {
+ public function inTransaction() {
return $this->transaction;
}
@@ -108,7 +113,13 @@ public function transactionRollback() {
*/
public function transactionAutoRollback() {
if($this->transaction)
- $this->rollbackTransaction();
+ $this->transactionRollback();
+ }
+
+ public function nextval ($name) {
+ $row = $this->selectOne ("SELECT nextval (%s) AS id", $name);
+ return $row->cast('i', 'id');
}
+
}
?>
\ No newline at end of file
diff --git a/classes/database/adapter/postgresql/db_PostgreSqlRow.php b/classes/database/adapter/postgresql/db_PostgreSqlRow.php
new file mode 100644
index 0000000..bf72e56
--- /dev/null
+++ b/classes/database/adapter/postgresql/db_PostgreSqlRow.php
@@ -0,0 +1,12 @@
+caster))
+ $this->caster = new caster_PostgreSqlToPhp();
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/database/vender/sqlite/db_SqlLite.php b/classes/database/adapter/sqlite/db_SqlLite.php
similarity index 100%
rename from classes/database/vender/sqlite/db_SqlLite.php
rename to classes/database/adapter/sqlite/db_SqlLite.php
diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php
index a437b13..f7d2df7 100644
--- a/classes/database/db_AbstractDatabase.php
+++ b/classes/database/db_AbstractDatabase.php
@@ -63,6 +63,10 @@ abstract class db_AbstractDatabase /* implements db_InterfaceDatabase */ {
*/
protected $caster;
+
+ protected $transaction;
+
+
/* CONSTRUCTOR & DESTRUCTOR */
/**
@@ -157,7 +161,7 @@ protected function connectReal($conString, $config) {
*/
protected function cast($args) {
if (is_null($this->caster))
- throw new db_Exception('Parser not loaded.');
+ throw new db_Exception('Caster not loaded.');
return $this->caster->castString(func_get_args());
}
@@ -170,6 +174,18 @@ public function disconnect() {
$this->pdo = null;
$this->connected = false;
}
+
+
+ public function formatResult ($rows) {
+
+ $rowArr = array();
+ foreach ($rows as $idx => $row)
+ $rowArr[] = $this->createRow($row);
+
+ return $rowArr;
+ }
+
+
/**
* Executes a basic query with casted variables
@@ -179,7 +195,7 @@ public function disconnect() {
*/
public function query($query, $args = null, $_ = null) {
$args = func_get_args();
- return $this->queryReal(call_user_func_array(array(&$this, 'cast'), $args));
+ return $this->formatResult($this->queryReal(call_user_func_array(array($this, 'cast'), $args)));
}
/**
@@ -207,8 +223,10 @@ public function queryReal($sql) {
throw new PDOException((isset($error[2]) ? $error[2] : 'Database Error: '.$error[0]));
}
- $data = $return->fetchAll(PDO::FETCH_OBJ);
-
+ $data = $return->fetchAll(PDO::FETCH_ASSOC);
+
+ $this->affectedRows = count($data);
+
if(!is_array($data))
throw new PDOException('Failed to return data array');
@@ -232,51 +250,93 @@ public function queryReal($sql) {
return $data;
} catch(PDOException $e) {
- throw new db_QueryFailedException($e->getMessage() . " " . $sql);
+ throw new db_QueryFailedException($e->getMessage() . " SQL:" . $sql);
}
}
- public function select($_) {
+
+
+ /*
+ * SELECT
+ * */
+ public function select($args) {
$args = func_get_args();
- return call_user_func_array(array(&$this, 'query'), $args);
+ return call_user_func_array(array($this, 'query'), $args);
}
-
- /**
- * Performs a query, returning a single result
- * @param $colums string The colum or colums to select
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_ Arg repeater (Repeat the last arg as many times as needed)
- * @return The query results
- */
- public function selectOne($colums, $tables, $where, $args = null, $_ = null) {
+ public function selectOne($args) {
$args = func_get_args();
- $result = call_user_func_array(array(&$this, 'select'), $args);
+ $result = call_user_func_array(array($this, 'select'), $args);
if(count($result) > 1)
- throw new db_QueryFailedException('selectOne returned more than one result');
+ throw new db_UnexpectedResultException('selectOne returned more than one result');
return array_key_exists(0, $result) ? $result[0] : null;
}
- /**
- * Performs a query, returning all colums of one result in an array
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_ Arg repeater (Repeat the last arg as many times as needed)
- * @return The query results
- */
- public function fetchOne($tables, $where, $args = null, $_ = null) {
+
+
+
+ /* FETCH
+ * abstract select - for simple queries (no joins) */
+ public function fetch ($cols, $table, $where = null, $orderBy = null, $offset = null, $limit = null) {
+ /* parse the query */
$args = func_get_args();
- array_unshift($args, '*');
+ $query = call_user_func_array (array ($this, 'parseFetchQuery'), $args);
- return call_user_func_array(array(&$this, 'selectOne'), $args);
+ /* perform query */
+ $result = $this->query('%l', $query);
+
+ return $result;
}
+ public function fetchOne($cols, $table, $where = null) {
+ $args = func_get_args();
+ $result = call_user_func_array(array($this, 'fetch'), $args);
+ if(count($result) > 1)
+ throw new db_UnexpectedResultException('fetchOne returned more than one result');
+ return array_key_exists(0, $result) ? $result[0] : null;
+ }
+ public function parseFetchQuery($cols, $table, $where = null, $orderBy = null, $offset = null, $limit = null) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
+ // strip out the nulls
+ $argsReal = func_get_args ();
+ $args = array();
+ foreach ($argsReal as $arg)
+ $args[] = !is_null($arg)?$arg:'';
+ $cols = array_shift ($args);
+ $table = array_shift ($args);
+
+ // handle offset limit
+ $offset = null;
+ $limit = null;
+
+ if (is_int(end($args))) $limit = array_pop($args);
+ if (is_int(end($args))) $offset = array_pop($args);
+
+ if (count($args) && $args[0] !== null) {
+ $sets = $this->caster->castArraySets($args);
+ $where = array_shift ($sets);
+ }
+ if (count($args) && $args[0] !== null) {
+ $orderBy = array_shift ($sets);
+ }
+ // return select query string
+ return $this->caster->castString(
+ 'SELECT %l FROM %@%l%l%l%l', $cols, $table,
+ empty($where)?'':$this->caster->castString(' WHERE %l', $where),
+ empty($orderBy)?'':$this->caster->castString(' ORDER BY %l', $orderBy),
+ !is_int($offset)?'':$this->caster->castString(' OFFSET %i', $offset),
+ !is_int($limit)?'':$this->caster->castString(' LIMIT %i', $limit)
+
+ );
+ }
+
+
/**
- * Performs a insert query
+ * INSERT
*
* Example:
* $db->insert('tableA', 'column1 = %s', 'a string', 'column2 = %i', 12345);
@@ -288,42 +348,61 @@ public function fetchOne($tables, $where, $args = null, $_ = null) {
* @throws db_Exception If the number of values does not match the number of columns
*/
public function insert($table, $column, $value = null, $_ = null) {
+ /* parse the query */
$args = func_get_args();
+ $query = call_user_func_array (array ($this, 'parseInsertQuery'), $args);
- $table = array_shift($args);
-
- // If there are still args left we have variable pairs
- $names = array();
- $types = array();
- $values = array();
- while(count($args) >= 2) {
- $column = array_shift($args);
-
- $column = explode('=', $column, 2);
+ /* perform query */
+ $this->query('%l', $query);
- $names[] = trim($column[0]);
- $types[] = trim($column[1]);
- $values[] = array_shift($args);
+ return true;
+ }
+ public function insertOrUpdateOne ($table, $where, $values) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
+ $args = func_get_args();
+ $sets = $this->caster->castArraySets($args);
+ $table = $sets [0];
+ $where = $sets [1];
+
+ $exists = $this->exists ($table, '%l', $where);
+
+ call_user_func_array (array ($this, $exists? 'updateOne' : 'insert'), $args);
+
+ }
+ public function deleteAndInsert ($args) {
+ throw new Expcetion ('TODO');
+ }
+ public function parseInsertQuery($args) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
+ $args = func_get_args();
+ $table = array_shift($args);
+
+
+ $rows = $this->caster->castArraySets($args);
+ $column = array();
+ $data = array();
+
+ foreach($rows as $row) {
+ $rowParts = preg_split('/=/', $row, 2);
+ $column[] = trim($rowParts[0]);
+ $data[] = trim($rowParts[1]);
}
- if(count($args) > 0)
- throw new db_Exception('Uneven number of column value paires');
-
- /* parse values */
- $valueString = implode(', ', $types);
- array_unshift($values, $valueString);
- $valueString = call_user_func_array(array($this, 'parse'), $values);
-
-
- $query = $this->parse(
- 'INSERT INTO %@ (%l) VALUES(%l)', $table, implode(', ', $names), $valueString
+ return $this->caster->castString(
+ 'INSERT INTO %@ (%l) VALUES(%l)', $table, implode(', ', $column), implode(', ', $data)
);
-
- $this->queryReal($query);
}
- // TODO: public function insert($table, $column, $value = null, $_ = null)
-
+ /*
+ * UPDATE
+ *
+ * */
public function update($args) {
/* parse the query */
$args = func_get_args();
@@ -334,54 +413,126 @@ public function update($args) {
return true;
}
-
public function updateOne($args) {
/* call update */
$args = func_get_args ();
$ret = call_user_func_array (array ($this, 'update'), $args);
// ensure affected row count is correct
- if($this->affected_rows () == 0)
- throw new sql_Exception ('No rows affected in updateOne()');
- if($this->affected_rows () > 1)
- throw new sql_Exception ('Multiple rows affected in updateOne()');
+ if($this->getAffectedRows () == 0)
+ throw new db_UnexpectedResultException ('No rows affected in updateOne()');
+ if($this->getAffectedRows () > 1)
+ throw new db_UnexpectedResultException ('Multiple rows affected in updateOne()');
return true;
}
-
public function parseUpdateQuery($args) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
$args = func_get_args ();
- $sets = $this->quotef_special($args);
+ $sets = $this->caster->castArraySets($args);
$update = array_shift ($sets);
$where = array_shift ($sets);
if (count ($sets) == 0) $sets = array ($where);
-
+
/* return update query string */
- return $this->format(
- 'UPDATE %l SET %l WHERE %l', $update, implode(', ', $sets), $where
+ return $this->caster->castString(
+ 'UPDATE %@ SET %l WHERE %l', $update, implode(', ', $sets), $where
);
}
- /* DEPRECATED METHODS */
+ /*
+ * EXISTS
+ * */
+ public function exists ($table, $where) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
+ $args = func_get_args();
+ $table = array_shift($args);
+ $where = $this->caster->castArraySets($args);
+
+ $result = $this->query(
+ 'SELECT CASE WHEN EXISTS (
+ SELECT *
+ FROM %@
+ WHERE %l
+ ) THEN %b ELSE %b END AS exists',
+ $table, $where[0], true, false
+ );
+
+ $exists = $result[0];
+ return $exists->cast('b', 'exists');
+ }
+
+ /*
+ * COUNT
+ * */
+ public function count ($table, $where = null) {
+
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
+ $args = func_get_args();
+ $table = array_shift($args);
+
+ $where = (count($args) && !is_null($args[0]))?$this->caster->castArraySets($args):array('1=1');
+
+ $result = $this->query(
+ 'SELECT COUNT(*) AS count FROM %@ WHERE %l',
+ $table, $where[0]
+ );
+
+ $exists = $result[0];
+ return $exists->cast('i', 'count');
+ }
- /**
- * Selects a single record from the database
- * @deprecated Back compatability functions. DO NOT USE!
- * @param unknown_type $_
- */
- public function select_1($_) {
- $args = func_get_args ();
- return call_user_func_array (array (&$this, 'selectOne'), $args);
- }
- /**
- * Updates a single record in the database.
- * @deprecated Back compatability functions. DO NOT USE!
- * @param unknown_type $_
- */
- public function update_1($_) {
+
+ /*
+ * DELETE
+ * */
+ public function delete ($args) {
+ /* parse the query */
+ $args = func_get_args();
+ $query = call_user_func_array (array ($this, 'parseDeleteQuery'), $args);
+
+ /* perform query */
+ $this->query('%l', $query);
+
+ return true;
+ }
+ public function deleteOne ($args) {
+ throw new db_Exception ('TODO');
+ }
+ public function parseDeleteQuery ($args) {
+ if (is_null($this->caster))
+ throw new db_Exception('Caster not loaded.');
+
$args = func_get_args ();
- return call_user_func_array (array (&$this, 'updateOne'), $args);
+ $sets = $this->caster->castArraySets($args);
+ $deleteFrom = array_shift ($sets);
+ $where = array_shift ($sets);
+
+ /* return update query string */
+ return $this->caster->castString(
+ 'DELETE FROM %@ WHERE %l', $deleteFrom, $where
+ );
+
}
+
+ /*
+ * Table management
+ * */
+ public function createTable ($args) {
+ throw new db_Exception ('TODO');
+ }
+ public function dropTable ($args) {
+ throw new db_Exception ('TODO');
+ }
+
}
?>
\ No newline at end of file
diff --git a/classes/database/db_AbstractRow.php b/classes/database/db_AbstractRow.php
new file mode 100644
index 0000000..3ea87ff
--- /dev/null
+++ b/classes/database/db_AbstractRow.php
@@ -0,0 +1,39 @@
+ 'utility/calendar'
+));
+
+abstract class db_AbstractRow {
+
+ private $rowData;
+ protected $caster;
+
+ abstract protected function initCaster ();
+
+ public function __construct ($rowData) {
+ $this->rowData = $rowData;
+ }
+ public function cast ($format, $column) {
+
+ if (!array_key_exists($column, $this->rowData))
+ throw new db_RowColumnNotFoundException('Column not found: '.$column);
+
+ $data = $this->rowData[$column];
+ $this->initCaster();
+
+ return $this->caster->cast('%'.$format, $data);
+
+ }
+ public function getRaw ($column) {
+ return $data[$column];
+ }
+
+ public function __get($call) {
+ $pos = strpos($call, '_');
+ return call_user_func_array (array($this, 'cast'), array(substr($call,0,$pos), substr($call,$pos+1)));
+
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/database/exceptions/db_RowColumnNotFoundException.php b/classes/database/exceptions/db_RowColumnNotFoundException.php
new file mode 100644
index 0000000..6882c0e
--- /dev/null
+++ b/classes/database/exceptions/db_RowColumnNotFoundException.php
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/classes/database/exceptions/db_UnexpectedResultException.php b/classes/database/exceptions/db_UnexpectedResultException.php
new file mode 100644
index 0000000..cfd8a2e
--- /dev/null
+++ b/classes/database/exceptions/db_UnexpectedResultException.php
@@ -0,0 +1,22 @@
+extendedInfo = $extendedInfo;
+ parent::__construct('Unexpected result from database query');
+ }
+
+ public function getInstructions($contentType) {
+ return $this->extendedInfo;
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/database/vender/mysql/db_MySql.php b/classes/database/vender/mysql/db_MySql.php
deleted file mode 100755
index 24d067b..0000000
--- a/classes/database/vender/mysql/db_MySql.php
+++ /dev/null
@@ -1,408 +0,0 @@
-connectReal($conString, $config);
- }
-
- /* CHARACTER ESCAPE FUNCTIONS */
-
- /**
- * Transforms a array into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted array.
- */
- protected function a($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms a boolean into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted boolean.
- */
- protected function b($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms a date into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted date.
- */
- protected function d($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms a string into a query literal ecripted representation.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped, quoted and encripted string.
- */
- protected function e($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms a float into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted float.
- */
- protected function f($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms a string into a query literal hash.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped, quoted and hashed string.
- */
- protected function h($val, $nullValid = false) {
- return $val;
- }
-
- /**
- * Transforms an integer into a query literal.
- * @param int $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted integer.
- */
- protected function i($val, $nullValid = false) {
- $val =(int)$val;
-
- if(!$nullValid && !is_int($val))
- throw new db_TypeException('Value not an Integer');
- elseif(!is_int($val) || is_null($val))
- throw new db_TypeException('Value not an Integer or Null '.$val);
-
- return sf('%s',(is_null($val) ? 'NULL' : $val));
- }
-
- /**
- * Transforms a string into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted string.
- */
- protected function s($val, $nullValid = false) {
- return sf("'%s'", $val);
- }
-
- /**
- * Transforms a timestamp into a query literal.
- * @param string $val The value to escape.
- * @param bool $nullValid If the value can be null
- * @return string The escaped and quoted timestamp.
- */
- protected function t($val, $nullValid = false) {
- return $val;
- }
-
- /* TRANSACTION FUNCTIONS */
-
- /**
- * Returns true if the database supports transaction
- *
- * @return boolean If the database supports transactions
- */
- public function transactionSupport() {
- return true;
- }
-
- /**
- * Returns true if the database is in transaction
- *
- * @return boolean If the database is in transaction
- */
- public function transaction() {
- return $this->transaction;
- }
-
- /**
- * Begins a database transaction. Will fail if there is already
- * a transaction in progress
- *
- * @return boolean If beginning the transaction was successful
- */
- public function transactionBegin() {
- if($this->transaction)
- throw new db_Exception('Cannot call beginTransaction() while already in transaction');
-
- $this->query('BEGIN');
- $this->transaction = true;
- return true;
- }
-
- /**
- * Commits a database transaction. Will fail if there is no
- * transaction in progress
- *
- * @return boolean If commiting the transaction was successful
- */
- public function transactionCommit() {
- if(!$this->transaction)
- throw new db_Exception('Cannot call commitTransaction() while not in transaction');
-
- $this->transaction = false;
- return $this->query('COMMIT');
- }
-
- /**
- * Rolls back a database transaction. Will fail if there is no
- * transaction in progress
- *
- * @return boolean If rolling back the transaction was successful
- */
- public function transactionRollback() {
- if(!$this->transaction)
- throw new db_Exception('Cannot call rollbackTransaction() While not in transaction');
-
- $this->transaction = false;
- return $this->query('ROLLBACK');
- }
-
- /**
- * Rolls back a database transaction, if there is one in
- * progress. This is designed for use in catch blocks to ensure
- * a rollback in case of error
- *
- * @return null
- */
- public function transactionAutoRollback() {
- if($this->transaction)
- $this->rollbackTransaction();
- }
-
- /* SELECT QUERIES */
-
- /**
- * Performs a query, returning all results in an array
- *
- * @param $colums string The colum or colums to select
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_
- * @return The query results
- */
- public function select($colums, $tables, $where = null, $args = null, $_ = null) {
- $args = func_get_args();
-
- $colums = array_shift($args);
- $tables = array_shift($args);
-
- // If there are still args left we have a where clause and possible where args
- if(count($args) > 0)
- $where = array_shift($args);
-
- if(is_array($colums))
- $colums = implode(', ', $colums);
-
- $query = sf(
- 'SELECT %s FROM %s%s',
- $colums,
- $tables,
- (isset($where) ? sf(' WHERE %s', $where) : '')
- );
-
- array_unshift($args, $query);
-
- return call_user_func_array(array(&$this, 'query'), $args);
- }
-
- /**
- * Performs a query, returning all colums of all results in an array
- *
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_
- * @return The query results
- */
- public function fetch($tables, $where = null, $args = null, $_ = null) {
- $args = func_get_args();
-
- array_unshift($args, '*');
-
- return call_user_func_array(array(&$this, 'select'), $args);
- }
-
- /**
- * Checks for the existance of a record
- *
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_
- * @return boolean If the row exists
- */
- public function exists($tables, $where = null, $args = null, $_ = null) {
- $args = func_get_args();
-
- $tables = array_shift($args);
-
- // If there are still args left we have a where clause and possible where args
- if(count($args) > 0)
- $where = array_shift($args);
-
- $query = sf(
- 'SELECT EXISTS(SELECT * FROM %s%s) AS \'exists\'',
- $tables,
- (isset($where) ? sf(' WHERE %s', $where) : '')
- );
-
- array_unshift($args, $query);
-
- $result = call_user_func_array(array(&$this, 'query'), $args);
-
- return(bool)$result[0]->exists;
- }
-
- /**
- * Counts the number of records
- *
- * @param $tables string The table or tables to query from
- * @param $where string The clause to query by
- * @param $args mixed Any args that should be used in the clause
- * @param $_
- * @return integer The number of records
- */
- public function count($tables, $where = null, $args = null, $_ = null) {
- $args = func_get_args();
-
- array_unshift($args, 'COUNT(*) as \'count\'');
-
- $result = call_user_func_array(array(&$this, 'select'), $args);
-
- return(int)$result[0]->count;
- }
-
- /* INSERT/UPDATE/DELETE QUERIES */
-
- public function insert($table, $column, $value = null, $_ = null) {
- $args = func_get_args();
-
- $table = array_shift($args);
-
- // If there are still args left we have variable pairs
- $names = array();
- $types = array();
- $values = array();
- while(count($args) >= 2) {
- $column = array_shift($args);
-
- $column = explode('=', $column, 2);
-
- $names[] = trim($column[0]);
- $types[] = trim($column[1]);
- $values[] = array_shift($args);
- }
-
- if(count($args) > 0)
- throw new db_Exception('Uneven number of column value paires');
-
- $query = sf(
- 'INSERT INTO %s(%s) VALUES(%s)',
- $table,
- implode(', ', $names),
- implode(', ', $types)
- );
-
- array_unshift($values, $query);
-
- return call_user_func_array(array(&$this, 'query'), $values);
- }
-
- public function insertBulk($table, $columns, $data) {}
-
- public function insertIfNotExists($table, $where, $args = null, $_ = null) {}
-
- public function insertOrUpdateOne($table, $where, $args = null, $_ = null) {}
-
- public function update($table, $where, $args = null, $_ = null, $column = null, $value = null, $_ = null) {
- $args = func_get_args();
-
- $table = array_shift($args);
- $where = array_shift($args);
-
- $where_arg_count = preg_match_all('/%[a-zA-Z]+/', $where, $matches);
- if(count($args) < $where_arg_count)
- throw new db_Exception('Incorrect number of args for where clause');
-
- $where_args = array_slice($args, 0, $where_arg_count);
- $args = array_slice($args, $where_arg_count);
-
- $names = array();
- $values = array();
- while(count($args) >= 2) {
- $names[] = array_shift($args);
- $values[] = array_shift($args);
- }
-
- if(count($args) > 0)
- throw new db_Exception('Uneven number of column value paires');
-
- $query = sf(
- 'UPDATE %s SET %s WHERE %s', $table, implode(', ', $names), $where
- );
-
- $query = array_merge(array($query), $values, $where_args);
-
- return call_user_func_array(array(&$this, 'query'), $query);
- }
-
- public function updateOne($table, $where, $args = null, $_ = null, $column = null, $value = null, $_ = null) {}
-
- public function delete($table, $where, $args = null, $_ = null) {}
-
- public function deleteOne($table, $where, $args = null, $_ = null) {}
-
- public function deleteAndInsert($table, $where, $args = null, $_ = null) {}
-
- /* DATA DEFINITION */
-
- public function tableCreate($table, $columns, $data = null) {}
-
- public function tableDrop($table) {}
-}
-?>
\ No newline at end of file
diff --git a/classes/helpers/http/atsumi_Http.php b/classes/helpers/http/atsumi_Http.php
deleted file mode 100644
index 23a2231..0000000
--- a/classes/helpers/http/atsumi_Http.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
\ No newline at end of file
diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php
index 9272ad4..07d9e03 100644
--- a/classes/mvc/controllers/mvc_AbstractController.php
+++ b/classes/mvc/controllers/mvc_AbstractController.php
@@ -12,12 +12,19 @@ abstract class mvc_AbstractController {
// GET FUNCTIONS
+ public function has($key) {
+ return (array_key_exists($key, $this->data));
+ }
+
public function get($key) {
if(!array_key_exists($key, $this->data))
- throw new Exception("Item does not exist in view data..");
+ throw new Exception(sf("'%s' does not exist in view data.",$key));
return $this->data[$key];
}
+ function __get ($key) {
+ return $this->get($key);
+ }
public function getViewHandler() {
return $this->viewHandler;
}
@@ -127,9 +134,12 @@ public function reload($anchor = null) {
$anchor = !is_null($anchor) ? "#".$anchor:"";
+ if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') $protocol = 'http://';
+ else $protocol = 'https://';
+
array_key_exists('REDIRECT_SCRIPT_URI',$_SERVER) ?
header('Location: ' . $_SERVER['REDIRECT_SCRIPT_URI']. $anchor)
- : header('Location: ' . 'http://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']. $anchor);
+ : header('Location: ' . $protocol . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']. $anchor);
exit;
}
@@ -137,14 +147,17 @@ public function reload($anchor = null) {
// redirect to a new page
public function redirect($url, $httpResponseCode = atsumi_Http::REDIRECT_FOUND) {
- if(substr($url, 0, 7) == "http://") {
+ if(substr($url, 0, 7) == "http://" || substr($url, 0, 8) == "https://") {
header('Location: ' . $url, true, $httpResponseCode);
exit;
}
+ if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') $protocol = 'http://';
+ else $protocol = 'https://';
+
$domain = $_SERVER['HTTP_HOST'];
unset($_POST);
- header('Location: ' . 'http://' . $domain.$url, true, $httpResponseCode);
+ header('Location: ' . $protocol . $domain.$url, true, $httpResponseCode);
exit;
}
@@ -169,21 +182,33 @@ public function getData($method = null, $controller = null) {
// TODO: This only works for page_ not other methods....
- $method = is_null($method)?substr($entity['method'][count($entity['method'])-1]['name'],5):$method;
+ // is method is an index?
+ if (is_integer($method))
+ $method = $method = substr($entity['method'][count($entity['method'])-1-$method]['name'],5);
+
+ // is it null?
+ else if (is_null($method))
+ $method = substr($entity['method'][count($entity['method'])-1]['name'],5);
+
$methodName = sf('data_%s',$method);
$pageMethod = null;
- // loop through methods to find the corect one...
- foreach($entity['method'] as $methodData)
- if($methodData['name'] == sf('page_%s',$method)) {
- $pageMethod = $methodData;
- break;
- }
+
+ // loop through methods to find the correct one...
+ foreach($entity['method'] as $methodData)
+ if($methodData['name'] == sf('page_%s',$method)) {
+ $pageMethod = $methodData;
+ break;
+ }
// TODO: Give this it's own exceptions!
if(is_null($methodData)) throw new Exception("Method not found...");
return call_user_func_array(array($controller, $methodName), $methodData['args']);
}
+
+ public function processRequest ($method, $args) {
+ return call_user_func_array(array($this, $method), $args);
+ }
}
?>
\ No newline at end of file
diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php
new file mode 100644
index 0000000..eb78a3e
--- /dev/null
+++ b/classes/mvc/models/core/mvc_LocaleModel.php
@@ -0,0 +1,111 @@
+ array(
+ 'type' => 's',
+ ),
+ 'country' => array(
+ 'type' => 's',
+ ),
+ 'localiseUris' => array(
+ 'type' => 'b',
+ 'default' => true
+ )
+ );
+
+ function getLocaleString () {
+ return sf('%s%s',
+ strtolower($this->get('language')),
+ $this->has('country')?
+ sf('-%s', strtolower($this->get('country'))):''
+ );
+ }
+
+ function uri ($uri) {
+
+ if (!$this->get('localiseUris')) return $uri;
+
+ return sf('/%s%s%s',
+ $this->getLocaleString(),
+ substr($uri, 0, 1) !== '/'?'/':'',
+ $uri
+ );
+ }
+ static public function fromLocaleString ($localeString) {
+
+ $locale = new self();
+ preg_match('/([a-z]{2})(-([a-zA-Z]{2}))*/i',
+ $localeString,
+ $matches
+ );
+ if (array_key_exists(1, $matches))
+ $locale->set('language', strtolower($matches[1]));
+
+ if (array_key_exists(3, $matches))
+ $locale->set('country', strtolower($matches[3]));
+
+ return $locale;
+
+ }
+ static function parseHttpAcceptLanguage ($httpAcceptLanguage) {
+
+ $langs = array();
+
+ // break up string into pieces (languages and q factors)
+ preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
+ $httpAcceptLanguage,
+ $langMatches
+ );
+
+ if (count($langMatches[1])) {
+ // create a list like "en" => 0.8
+ $langs = array_combine($langMatches[1], $langMatches[4]);
+
+ // set default to 1 for any without q factor
+ foreach ($langs as $lang => $val) {
+ if ($val === '') $langs[$lang] = 1;
+ }
+
+ // sort list based on value
+ arsort($langs, SORT_NUMERIC);
+ $langs = array_change_key_case($langs, CASE_LOWER);
+ }
+
+ return $langs;
+ }
+ static function interpolate ($str, $vars) {
+ $originalStr = $str;
+ while (preg_match('/\$\{(.*?)\}/sm', $str, $m)) {
+ list($src, $var) = $m;
+
+ // retrieve variable to interpolate in context, throw an exception
+ // if not found.
+
+ if (!array_key_exists($var, $vars)) {
+
+ // TODO: Add a custom exception
+ Atsumi::error__listen(
+ new Exception (
+ sf('Locale interpolation failed in for var: "%s" in str: "%s"',
+ $var, $originalStr
+ )
+ )
+ );
+ $value = '';
+ } else $value = $vars[$var];
+ $str = str_replace($src, $value, $str);
+ }
+
+ foreach ($vars as $key => $replacement) {
+ $str = str_replace ('${'.$key.'}', $replacement, $str);
+ }
+ return $str;
+
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/mvc/models/mvc_AbstractDaoModel.php b/classes/mvc/models/mvc_AbstractDaoModel.php
index b8690a5..3fbc42a 100644
--- a/classes/mvc/models/mvc_AbstractDaoModel.php
+++ b/classes/mvc/models/mvc_AbstractDaoModel.php
@@ -1,77 +1,128 @@
select($this->selectQuery);
- $returnArray = array();
-
- foreach( $results as $row ) {
- $forum = new gts_ForumAreaModel($db);
- $forum->loadFromSqlRow($row);
- $returnArray[] = $forum;
+abstract class mvc_AbstractDaoModel extends mvc_AbstractModel {
+
+
+ static public function write ($db, $o) {
+
+
+ $method = !is_null($o->get('id', false))?'update':'insert';
+
+ $args = array();
+
+ if ($method == 'update') {
+ $args[] = sf('%s = %s', 'id', $o->get('id'));
}
- return $returnArray;
- }
- abstract protected function getSelectQuery();
+// $vars = $this->getChanges();
+ foreach($o->data as $key => $value) {
+ if (!$o->structure[$key]['write']) continue;
+ $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']);
+ $args[] = $value;
+ }
+
+ if (!count($args))
+ throw new Exception ('Model has nothing to write...');
+
+ array_unshift($args, static::DB_TABLE_NAME);
+
+ $result = call_user_func_array(array($db, $method), $args);
+
+ if (is_null($o->get('id', false))) {
+ $id = $db->selectOne('select lastval()');
+ $o->set('id', $id->i_lastval, true);
+ }
+
+ }
- final public function __construct($db) {
-
+ /*
+ static public function writeInsert ($db, $o) {
+
+ $args = array();
+ foreach($o->data as $key => $value) {
+ if (!$o->structure[$key]['write']) continue;
+ $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']);
+ $args[] = $value;
+ }
+
+ array_unshift($args, static::DB_TABLE_NAME);
+
+ $result = call_user_func_array(array($db, 'insert'), $args);
}
- static public function load($db, $id) {
- if(!is_int($id)) throw new Exception("Identifier must be of type Integer");
- die(get_class());
- $file = new gts_ForumAreaModel($db);
- $file->loadFromId($id);
- return $file;
+ */
+
+ /* generic */
+ static protected function load ($db, $where = null, $orderBy = null, $offset = null, $limit = null) {
+
+ if (is_null($where)) $where = '';
+ else $where = ' where '.$where;
+
+ if (is_null($orderBy)) $orderBy = '';
+ else $orderBy = ' order by '.$orderBy;
+
+ if (is_null($offset)) $offset = '';
+ else $offset = ' offset '.$offset;
+
+ if (is_null($limit)) $limit = '';
+ else $limit = ' limit '.$limit;
+
+ $rows = $db->select('%l %l %l %l %l',
+ static::DB_SELECT_QUERY, $where, $orderBy, $offset, $limit);
+
+ $arrOut = array();
+
+ if (!$rows) return $arrOut;
+
+ foreach ($rows as $row) {
+ $obj = new static();
+ $obj->populateFromSqlRow($row);
+ $arrOut[] = $obj;
+ }
+
+ return $arrOut;
+
+ }
+
+ /* generic */
+ function populateFromSqlRow ($r) {
+ $rowData = $r->getData();
+
+ // itterate through the db columns and populate the object
+ foreach ($rowData as $k => $v) {
+
+ // check we're expecting the currently column
+ if (!array_key_exists($k, $this->structure))
+ throw new Exception (sf('Unexpected row column "%s", add this to the models \'structure\' member variable.', $k));
+
+ try {
+
+ $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v);
+
+ // if casting error throw a useful exception
+ } catch (Exception $e) {
+ $spec = caster_PostgreSqlToPhp::getSpec();
+
+ if (!array_key_exists($this->structure[$k]['type'], $spec))
+ throw new caster_Exception (
+ sf("Caster doesn't support format: %%%s",
+ $this->structure[$k]['type']
+ )
+ );
+
+ throw new caster_Exception (
+ sf("%s: '%s' could not be cast as '%s' (%%%s)",
+ $k,
+ is_null($v)?'NULL':$v,
+ $spec[$this->structure[$k]['type']],
+ $this->structure[$k]['type']
+ )
+ );
+ }
+ }
}
-
- public function loadFromId($id) {
-
- $row = $this->db->select_1("
- SELECT f.id, f.path, f.user_id, f.size, f.memorial_id, u.namefirst AS user_firstname,
- u.namelast AS user_lastname, u.vc_urllocation AS user_url,
- f.order, mime_type, uploaded, caption
- FROM file f
- INNER JOIN users u ON f.user_id = u.id
- WHERE f.id = %i ", $id);
-
- $this->loadFromSqlRow($row);
-
- }
-
-}
-/*
- * ORM Implementation - put on ice
- *
-class gts_ForumAreaModel extends orm_AbstractModel {
- public function __construct() {
-
- // TODO : This is only here as PHP won't let this be declared
- // by default...
-
- $this->id = new orm_DataType_Integer();
- $this->name = new orm_DataType_Text();
- $this->sef_name = new orm_DataType_Text();
- $this->description = new orm_DataType_Text();
-
-
-
- $this->map("id", new orm_DataType_Integer());
- $this->map("name", new orm_DataType_Text());
- $this->map("sef_name", new orm_DataType_Text());
- $this->map("description", new orm_DataType_Text());
-
- }
}
- */
diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php
new file mode 100644
index 0000000..12a7fb3
--- /dev/null
+++ b/classes/mvc/models/mvc_AbstractModel.php
@@ -0,0 +1,307 @@
+_fromModel($data);
+ return $o;
+ }
+
+ private function _fromModel (self $data) {
+
+ foreach($this->structure as $key => $properties) {
+ switch ($properties['type']) {
+ case 'o':
+ if (isset($properties['model']) && $data->has($key)) {
+ $m = $properties['model']::from($data->get($key));
+
+ $this->set($key, $m, true);
+ break;
+
+ }
+ default:
+ $this->set($key, $data->get($key), true);
+
+ }
+
+ }
+ return $this;
+ }
+
+ /* generic */
+ public function __construct ($data = array()) {
+
+ foreach ($this->structure as $k => $properties) {
+ switch ($properties['type']) {
+
+ case 'o':
+
+ // do the objects want to be created
+ if (isset($properties['create']) && $properties['create'] == true) {
+
+ if (isset($properties['model']) && !is_null($properties['model'])) {
+ $this->set($k, new $properties['model']);
+
+ // if it's a dynamic model - create it
+ } else if (isset($properties['structure']) && !is_null($properties['structure'])) {
+ $this->set($k, new mvc_DynamicModel($properties['structure']));
+ }
+
+ }
+
+ }
+
+ $this->set(
+ $k,
+ isset($properties['default'])?
+ $properties['default']:null,
+ true
+ );
+
+
+ }
+
+ // set data
+ foreach ($data as $k => $value) {
+ $this->set($k, $value);
+ }
+ }
+
+ function preOutput($outputType) { }
+
+
+
+ /* generic */
+ function has ($k) {
+ return array_key_exists($k, $this->data) && isset($this->data[$k]);
+ }
+
+ /* generic */
+ function set ($k, $v, $force = false) {
+
+ if (!$force &&
+ (
+ isset($this->structure[$k]['write']) &&
+ $this->structure[$k]['write'] == false
+ )
+ )
+ throw new Exception ('Column not writable');
+
+
+ if (isset($this->structure[$k]['type']) &&
+ $this->structure[$k]['type'] == 'o' &&
+ isset($this->data[$k]) && $this->data[$k] instanceof mvc_AbstractModel
+ ) {
+ if (is_array($v)) {
+ $this->data[$k]->setArray($v);
+ } else if ($v instanceof mvc_AbstractModel) {
+ $this->data[$k] = $v;
+ } else if (is_null($v)) {
+ // $this->data[$k] = $v;
+ } else {
+ // at this point we don't have valud data for an object...
+ // dump(array('dont know what to do', $k, $v));
+ }
+ } else
+ $this->data[$k] = $v;
+ }
+
+
+ // setup the properties that are objects
+ // TODO: this shouldn't be required - __construct should this automatically...
+ function setupObjectsFromStructure ($structure) {
+ foreach ($structure as $k => $v)
+ if ($this->structure[$k]['type'] == 'o' && isset($v['structure'])) {
+ $o = new mvc_DynamicModel($v['structure']);
+ $o->setupObjectsFromStructure($v['structure']);
+ $this->set($k, $o);
+ }
+ }
+
+
+
+ function setArray ($assoc) {
+ foreach ($assoc as $k => $v)
+ $this->set($k, $v);
+ }
+
+ function increment ($k, $v = 1) {
+ if (!$this->has($k)) throw new Exception('unknown key: '.$k);
+
+ // increment different data types
+ switch ($this->structure[$k]['type']) {
+
+ // INTERVAL
+ case 'z':
+ case 'Z':
+
+ // check if it's null
+ if (is_null($this->data[$k]))
+ $this->data[$k] = new atsumi_Interval(0);
+
+ $this->data[$k]->add(new atsumi_Interval($v));
+ break;
+
+ // NUMERIC
+ case 'i':
+ case 'I':
+ case 'e':
+ case 'E':
+ case 'n':
+ case 'N':
+ case 'f':
+ case 'F':
+
+ // check if it's null
+ if (is_null($this->data[$k]))
+ $this->data[$k] = 0;
+
+ $this->data[$k] += $v;
+ break;
+
+
+ default:
+ throw new Exception("Unexpected data type to increment %".$this->structure[$k]['type']);
+
+ }
+ }
+
+ function getStructure () {
+ return $this->structure;
+ }
+
+ function __get ($key) {
+ return $this->get($key);
+ }
+
+ /* generic */
+ function get($key, $strict = true) {
+
+ if (array_key_exists($key, $this->data))
+ return $this->data[$key];
+
+ else if (!array_key_exists($key, $this->data) && !array_key_exists($key, $this->structure))
+ throw new Exception ('>>'.$key. '<< is not a known property of >>' .get_called_class().'<<');
+
+
+ else if (!array_key_exists($key, $this->data) && array_key_exists('default', $this->structure[$key]))
+ return $this->structure[$key]['default'];
+
+ else if (!array_key_exists($key, $this->data))
+ throw new Exception ('property: >>'.$key. '<< has no default value in >>' .get_called_class().'<<');
+
+
+
+ /*
+ * TODO: casting
+ try {
+ $value = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$key]['type']), $this->data[$key]);
+ return $value;
+ } catch (Exception $e) {
+ if ($strict) throw $e;
+ return null;
+ }
+ */
+ }
+
+ static function outputItem ($value, $type) {
+
+ // if abstract model then output that
+ if ($value instanceof mvc_AbstractModel)
+ return $value->output($type);
+
+ elseif (is_object($value) && method_exists($value, 'output'))
+ return $value->output($type);
+
+ // if array itterate through
+ elseif (is_array($value)) {
+ $arrayOut = array();
+ foreach ($value as $item)
+ $arrayOut[] = self::outputItem($item, $type);
+ return $arrayOut;
+
+ // otherwise just return value
+ } else
+ return $value;
+
+ }
+
+ function __toString () {
+ return pretty($this->output());
+ }
+
+ function output ($type = self::OUTPUT_FORMAT_ASSOC) {
+
+ $this->preOutput($type);
+
+ switch ($type) {
+
+ // Native object
+ case self::OUTPUT_FORMAT_OBJECT:
+
+ foreach($this->structure as $key => $properties) {
+ if (isset($this->structure[$key]['output']) &&
+ $this->structure[$key]['output'] == false)
+ continue;
+
+ self::outputItem($this->get($key), $type);
+ }
+
+ return $this;
+
+ // Associative array
+ case self::OUTPUT_FORMAT_ASSOC:
+ $out = array();
+ foreach($this->structure as $key => $properties) {
+ if (isset($this->structure[$key]['output']) &&
+ $this->structure[$key]['output'] == false)
+ continue;
+
+ $out[$key] = self::outputItem($this->get($key), $type);
+ }
+ return $out;
+
+ // Std Class
+ case self::OUTPUT_FORMAT_STD_CLASS:
+ $out = new stdClass();
+ foreach($this->structure as $key => $properties) {
+ if (isset($this->structure[$key]['output']) &&
+ $this->structure[$key]['output'] == false)
+ continue;
+
+ $out[$key] = self::outputItem($this->get($key), $type);
+ }
+ return $out;
+ }
+
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/mvc/models/mvc_DynamicModel.php b/classes/mvc/models/mvc_DynamicModel.php
new file mode 100644
index 0000000..4926787
--- /dev/null
+++ b/classes/mvc/models/mvc_DynamicModel.php
@@ -0,0 +1,50 @@
+ $properties) {
+ $this->add($k, $properties);
+
+ if (isset($properties['value']))
+ $data[$k] = $properties['value'];
+ elseif (isset($properties['default']))
+ $data[$k] = $properties['default'];
+ }
+
+ parent::__construct($data);
+ }
+
+
+
+ public function add ($key, $properties) {
+
+ $this->structure[$key] = array(
+ 'type' => $properties['type'],
+ 'create' => isset($properties['create'])?$properties['create']:false,
+ 'structure' => isset($properties['structure'])?$properties['structure']:null,
+ 'model' => isset($properties['model'])?$properties['model']:null,
+ 'default' => isset($properties['default'])?$properties['default']:null
+ );
+
+ if (isset($properties['default']))
+ $this->set($key, $properties['default']);
+
+ if (isset($properties['value']))
+ $this->set($key, $properties['value']);
+
+ }
+
+ // is defined?
+ public function exists ($key) {
+ return array_key_exists($key, $this->structure);
+ }
+
+
+}
+?>
\ No newline at end of file
diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php
new file mode 100644
index 0000000..364b442
--- /dev/null
+++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php
@@ -0,0 +1,208 @@
+ file path
+ private $templateFileMap = array();
+
+ // optional main template, template that includes $pageContent
+ private $mainTemplate = false;
+
+ // view/page data
+ private $viewData = array();
+
+ private $surpressErrors = true;
+
+ const TEMPLATE_TYPE_FILE = 1;
+ const TEMPLATE_TYPE_STRING = 2;
+
+
+ public function __construct($mainTemplate = false, $templateFileMap = array(), $surpressErrors = true) {
+
+ $this->templateFileMap = $templateFileMap;
+ $this->mainTemplate = $mainTemplate;
+ $this->surpressErrors = $surpressErrors;
+ }
+
+
+ /*
+ * Static: Processes a specific template file
+ */
+ static public function processTemplateFile ($templateFile, $data, $supressErrors = false) {
+ if (empty($templateFile)) return;
+
+ extract($data, EXTR_SKIP);
+ ob_start();
+ try {
+
+ if ($supressErrors)
+ @include($templateFile);
+ else
+ include($templateFile);
+
+ } catch (Exception $e) {
+
+ Atsumi::error__listen($e);
+
+ if (!$supressErrors)
+ throw $e;
+ else
+ Atsumi::error__recover($e);
+
+
+ /*
+ throw new mvc_ViewNotFoundException (
+ "Can't find referenced template: ".$templateFile,
+ $templateFile
+ ); */
+ }
+ return ob_get_clean();
+ }
+
+ /*
+ * Static: Processes a specific template string
+ * WARNING: You could create a PHP vulnerability by using this
+ */
+ static public function processTemplateString ($templateString, $data, $supressErrors = false) {
+
+ if (empty($templateString) || $templateString == '' || !$templateString) return;
+
+ extract($data, EXTR_SKIP);
+ ob_start();
+ try {
+
+ eval("?>".$templateString.'viewData);
+
+ if ($templateFile) {
+
+ if (array_key_exists($templateFile, $this->templateFileMap))
+ $templateFile = $this->templateFileMap[$templateFile];
+
+
+ return self::processTemplateFile($templateFile, $data, $this->surpressErrors);
+
+ } else if ($templateString) {
+
+ return self::processTemplateString($templateString, $data, $this->surpressErrors);
+
+ } else {
+ throw new Exception ('Unknown template type: '.$template);
+ }
+ }
+
+
+ /*
+ * prints a template-ref/template-file
+ * optionally includes view data
+ */
+ public function renderTemplate ($template, $data = array(), $incViewData = false) {
+ print $this->processTemplate($template, $data, $incViewData);
+ }
+
+
+ /*
+ * returns processed template
+ * optionally uses main template
+ * $viewTemplate can be type string (file path) or array
+ */
+ public function process ($viewTemplate, $viewData, $mainTemplateOverride = null) {
+
+ $this->viewData = $viewData;
+ $mainTemplate = is_null($mainTemplateOverride)?
+ $this->mainTemplate:
+ $mainTemplateOverride;
+
+ // process the view tempalte
+ $pageContent = $this->processTemplate(
+ $viewTemplate,
+ array(),
+ true
+ );
+ // if there is a main template - process & render it
+ if ($mainTemplate)
+ return $this->processTemplate(
+ $mainTemplate,
+ array("pageContent"=>$pageContent),
+ true
+ );
+
+ // if not - render view template
+ else return $pageContent;
+ }
+
+ /*
+ * prints a processed
+ * optionally uses main template
+ */
+ public function render ($viewTemplate, $viewData, $mainTemplateOverride = null) {
+ print $this->process($viewTemplate, $viewData, $mainTemplateOverride);
+ }
+
+
+ /*
+ * set the main template
+ */
+ public function setMainTemplate ($in) {
+ $this->mainTemplate = $in;
+ }
+
+
+
+
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/mvc/view_handlers/mvc_SmartyViewHandler.php b/classes/mvc/view_handlers/mvc_SmartyViewHandler.php
index bf52385..858ce7e 100644
--- a/classes/mvc/view_handlers/mvc_SmartyViewHandler.php
+++ b/classes/mvc/view_handlers/mvc_SmartyViewHandler.php
@@ -16,7 +16,7 @@ public function render($viewName, $viewData) {
if(is_null($this->smarty))
throw new mvc_TemplateEngineNotSupplied("Smarty has not been supplied");
- $this->smarty->clear_all_assign();
+ $this->smarty->clearAllAssign();
$this->smarty->assign($viewData);
$this->smarty->display($viewName);
diff --git a/classes/mvc/views/mvc_HtmlView.php b/classes/mvc/views/mvc_HtmlView.php
index aafb059..9af3d42 100644
--- a/classes/mvc/views/mvc_HtmlView.php
+++ b/classes/mvc/views/mvc_HtmlView.php
@@ -10,6 +10,15 @@ abstract class mvc_HtmlView extends mvc_AbstractView {
abstract protected function renderBodyContent();
+
+ // headers
+ public function setHeaders() {
+ header(sf('Content-Type: text/html; charset=%s', $this->getCharset()));
+ }
+ public function getCharset() {
+ return 'utf-8';
+ }
+
public function render() {
$this->renderDoctype();
$this->renderHtml();
diff --git a/classes/mvc/views/mvc_JsonView.php b/classes/mvc/views/mvc_JsonView.php
index 863b88e..feed26b 100644
--- a/classes/mvc/views/mvc_JsonView.php
+++ b/classes/mvc/views/mvc_JsonView.php
@@ -9,4 +9,4 @@ public function setHeaders() {
header(sf('Content-Type: application/json; charset=%s', $this->getCharset()));
}
}
-?>
+?>
\ No newline at end of file
diff --git a/classes/mvc/views/mvc_JsonpView.php b/classes/mvc/views/mvc_JsonpView.php
new file mode 100644
index 0000000..e2009ab
--- /dev/null
+++ b/classes/mvc/views/mvc_JsonpView.php
@@ -0,0 +1,12 @@
+get_callback)?'callback':$this->get_callback, $this->get_json);
+ }
+
+ public function setHeaders() {
+ header(sf('Content-Type: application/json; charset=%s', $this->getCharset()));
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/mvc/views/mvc_TxtView.php b/classes/mvc/views/mvc_TxtView.php
index 076cfcc..6ef2d4a 100644
--- a/classes/mvc/views/mvc_TxtView.php
+++ b/classes/mvc/views/mvc_TxtView.php
@@ -1,14 +1,11 @@
get_txt);
}
@@ -16,11 +13,9 @@ public function render() {
// headers
public function setHeaders() {
header(sf('Content-Type: text/plain; charset=%s', $this->getCharset()));
+ header(sf('Content-Length: %s', strlen($this->get_txt)));
}
-
-
-
-
+
}
?>
diff --git a/classes/session/session_Handler.php b/classes/session/session_Handler.php
index 1d0daa7..f7a90f0 100644
--- a/classes/session/session_Handler.php
+++ b/classes/session/session_Handler.php
@@ -1,15 +1,40 @@
configure($options);
// Fetch the session storage handler
- $storage =(isset($options['storage']) ? $options['storage'] : self::DEFAULT_STORAGE);
+ $storage = (isset($options['storage']) ? $options['storage'] : self::DEFAULT_STORAGE);
if(is_subclass_of($storage, 'session_AbstractStorage'))
$this->storage = new $storage($options);
// Start the session
- $this->start();
+ $this->start($options);
// Add debug information
atsumi_Debug::record(
'Session Created',
- 'The atsumi session constructor completed', true,
+ 'The atsumi session constructor completed', null, true,
atsumi_Debug::AREA_SESSION
);
}
@@ -68,17 +95,14 @@ public function __destruct() {
} catch (Exception $e) { }
}
- public function getId() {
- return session_id();
- }
+ /* GET METHODS */
/**
* Returns a singleton instance of atsumi session
- *
* @param $options Session setup options
* @return object A session_Handler object
*/
- static public function getInstance($options = array()) {
+ public static function getInstance($options = array()) {
static $instance;
if(!is_object($instance))
@@ -87,8 +111,13 @@ static public function getInstance($options = array()) {
return $instance;
}
- /* GET FUNCTIONS */
-
+ /**
+ * Returns a variable from the current active session.
+ * @param string $name The name of the variable to return.
+ * @param mixed $default The variable to return if the value does not exist.
+ * @param string $namespace The namespace under which the variable is stored.
+ * @return mixed The variable under that name or the default.
+ */
public function &get($name, $default = null, $namespace = self::DEFAULT_NAMESPACE) {
// Add prefix to prevent namespace collisions
$namespace = '__'.$namespace;
@@ -99,7 +128,23 @@ public function &get($name, $default = null, $namespace = self::DEFAULT_NAMESPAC
return $default;
}
- /* SET FUNCTIONS */
+ /**
+ * Returns the current session id.
+ * @return string The session id.
+ */
+ public function getId() {
+ return session_id();
+ }
+
+ /* SET METHODS */
+
+ /**
+ * Sets a variable to the current active session and return the old value if set.
+ * @param string $name The name of the variable to return.
+ * @param mixed $value The variable to be stored under that name.
+ * @param string $namespace The namespace under which the variable is stored.
+ * @return mixed The old value under the name if it exists.
+ */
public function set($name, $value, $namespace = self::DEFAULT_NAMESPACE) {
// Add prefix to prevent namespace collisions
$namespace = '__'.$namespace;
@@ -114,6 +159,9 @@ public function set($name, $value, $namespace = self::DEFAULT_NAMESPACE) {
return $old;
}
+ /* MAGIC METHODS */
+ /* METHODS */
+
public function push($name, $value, $namespace = self::DEFAULT_NAMESPACE) {
// Add prefix to prevent namespace collisions
$namespace = '__'.$namespace;
@@ -125,18 +173,17 @@ public function push($name, $value, $namespace = self::DEFAULT_NAMESPACE) {
}
- /* HAS FUNCTIONS */
- public function has($name, $namespace = self::DEFAULT_NAMESPACE) {
+ public function exists($name, $namespace = self::DEFAULT_NAMESPACE) {
// Add prefix to prevent namespace collisions
$namespace = '__'.$namespace;
return isset($_SESSION[$namespace][$name]);
}
- public function del($name, $namespace = self::DEFAULT_NAMESPACE) {
+ public function delete($name, $namespace = self::DEFAULT_NAMESPACE) {
$this->set($name, null, $namespace);
}
- protected function start() {
+ protected function start($options = array()) {
if(headers_sent() === true)
throw new session_HeadersSentException;
@@ -146,6 +193,9 @@ protected function start() {
session_cache_limiter('none');
session_start();
+ if(isset($options['persistent']) && $options['persistent'] === true)
+ $this->renewCookie($options['life']);
+
$this->state = self::STATE_ACTIVE;
}
@@ -177,20 +227,51 @@ protected function configure($options = array()) {
ini_set('session.save_handler', 'files');
ini_set('session.use_trans_sid', false);
+ if(isset($options['life'])) ini_set('session.cookie_lifetime', $options['life']);
+
+ $this->configCookie($options);
+
if(isset($options['name']))
session_name($options['name']);
else
session_name('atsumi_session');
+ }
- if(isset($options['domain']))
- ini_set('session.cookie_domain', $options['domain']);
+ public function configCookie($options = array()) {
+ $old = session_get_cookie_params();
+ session_set_cookie_params(
+ (isset($options['life']) ? $options['life'] : $old['lifetime']),
+ (isset($options['cookie_path']) ? $options['cookie_path'] : $old['path']),
+ (isset($options['cookie_domain']) ? $options['cookie_domain'] : $old['domain']),
+ (isset($options['cookie_secure']) ? $options['cookie_secure'] : $old['secure']),
+ (isset($options['cookie_httponly']) ? $options['cookie_httponly'] : $old['httponly'])
+ );
}
protected function destroyCookie() {
+ $name = session_name();
+ if(!isset($_COOKIE[$name])) return;
+
$params = session_get_cookie_params();
- setcookie(session_name(), '', time() - 42000,
- $params["path"], $params["domain"],
- $params["secure"], $params["httponly"]
+ setcookie($name, '', time() - 42000,
+ $params['path'], $params['domain'],
+ $params['secure'], $params['httponly']
+ );
+ }
+
+ protected function renewCookie($life = 0) {
+ $name = session_name();
+ if(!isset($_COOKIE[$name])) return;
+
+ $params = session_get_cookie_params();
+ setcookie(
+ $name,
+ session_id(),
+ ($life > 0 ? (time() + $life) : 0),
+ $params['path'],
+ $params['domain'],
+ $params['secure'],
+ $params['httponly']
);
}
@@ -204,5 +285,15 @@ protected function generateId($len = 32) {
session_id($id);
return $id;
}
+
+ /* DEPRECATED METHODS */
+
+ public function has($name, $namespace = self::DEFAULT_NAMESPACE) {
+ $this->exists($name, $namespace);
+ }
+
+ public function del($name, $namespace = self::DEFAULT_NAMESPACE) {
+ $this->delete($name, $namespace);
+ }
}
?>
\ No newline at end of file
diff --git a/classes/session/storage/session_CacheStorage.php b/classes/session/storage/session_CacheStorage.php
new file mode 100644
index 0000000..d94670f
--- /dev/null
+++ b/classes/session/storage/session_CacheStorage.php
@@ -0,0 +1,55 @@
+cache = $cache;
+ $this->ttl = (isset($options['life']) ? $options['life'] : SELF::DEFAULT_MAX_LIFE);
+
+ ini_set('session.gc_divisor', 1);
+ ini_set('session.gc_maxlifetime', $this->ttl);
+ ini_set('session.gc_probability', 100);
+
+ parent::__construct($options);
+ }
+
+ public function read($id) {
+
+ atsumi_Debug::startTimer('SESSION_READ');
+
+ $result = $this->cache->get($id, null, 'SESSION');
+
+ if(is_null($result))
+ return '';
+
+ atsumi_Debug::record(
+ 'Session loaded from cache',
+ sf('Session ID: %s', $id),
+ $result,
+ 'SESSION_READ',
+ atsumi_Debug::AREA_SESSION,
+ $result
+ );
+ return $result;
+ }
+
+ public function write($id, $sessionData) {
+
+ $this->cache->set($id, $sessionData, $this->ttl, 'SESSION');
+ return true;
+ }
+
+ public function destroy($id) {
+ $this->cache->set($id, null, -1, 'SESSION');
+ return true;
+ }
+
+ public function gc($maxlifetime) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/classes/session/storage/session_DatabaseStorage.php b/classes/session/storage/session_DatabaseStorage.php
index 2e8c219..2c529a5 100644
--- a/classes/session/storage/session_DatabaseStorage.php
+++ b/classes/session/storage/session_DatabaseStorage.php
@@ -11,7 +11,7 @@ public function __construct($options) {
$this->database = $options['database'];
ini_set('session.gc_divisor', 1);
- ini_set('session.gc_maxlifetime', 1440);
+ ini_set('session.gc_maxlifetime', (isset($options['life']) ? $options['life'] : 7200));
ini_set('session.gc_probability', 100);
parent::__construct($options);
@@ -48,8 +48,6 @@ public function read($id) {
public function write($id, $sessionData) {
try {
if($this->database->exists('session', 'checksum = %i AND session_id = %s', crc32($id), $id)) {
-
-
atsumi_Debug::record(
'Updating Session',
sf('The session(%s) is being updated to the DB', $id ),
@@ -82,7 +80,7 @@ public function write($id, $sessionData) {
return true;
/* this is a little drastic but will most likley segfault if Exception bubbles up */
- } catch (Exception $e) { die("Could not write session to database."); }
+ } catch (Exception $e) { die('Could not write session to database.'); }
}
public function destroy($id) {
diff --git a/classes/sitemap/exceptions/sitemap_Exception.php b/classes/sitemap/exceptions/sitemap_Exception.php
new file mode 100644
index 0000000..c70c55b
--- /dev/null
+++ b/classes/sitemap/exceptions/sitemap_Exception.php
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/classes/sitemap/exceptions/sitemap_WriteException.php b/classes/sitemap/exceptions/sitemap_WriteException.php
new file mode 100644
index 0000000..b249cf3
--- /dev/null
+++ b/classes/sitemap/exceptions/sitemap_WriteException.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/classes/sitemap/sitemap_Handler.php b/classes/sitemap/sitemap_Handler.php
index 5bba858..ac8e27a 100644
--- a/classes/sitemap/sitemap_Handler.php
+++ b/classes/sitemap/sitemap_Handler.php
@@ -6,51 +6,60 @@ class sitemap_Handler {
/* deletes all urls for a host */
static public function purgeUrlsForHost($db, $host) {
-
$db->delete('sitemap', 'host = %s', $host);
}
/* adds a url to the sitemap table for inclusion next time the sitemap is generated */
static public function writeUrl(&$db, $loc, $lastMod = null, $changeFreq = null, $priority = null, $tableName = 'sitemap') {
- $loc = trim($loc);
- $checksum = crc32($loc);
-
- /* get the host name from url */
- preg_match('@^(?:http://)?([^/]+)@i', $loc, $matches);
- $host = $matches[1];
-
- $row = $db->select_1('select * from sitemap where checksum = \'%l\'::bigint AND loc = %s', $checksum, $loc);
-
- /* new location */
- if(is_null($row)) {
- $db->insert(
- 'sitemap',
- 'checksum = \'%l\'::bigint', $checksum,
- 'host = %s', $host,
- 'loc = %s', $loc,
- 'last_mod = %T', $lastMod,
- 'change_freq = %S', $changeFreq,
- 'priority = %l', is_null($priority)?'NULL':$priority
- );
-
- /* update as details have changed */
- } elseif($row->t_last_mod != $lastMod || $row->s_change_freq != $changeFreq || $row->s_priority != $priority) {
- $db->update_1(
- 'sitemap',
- 'checksum = \'%l\'::bigint AND loc = %s', $row->i_checksum, $loc,
- 'last_mod = %T', $lastMod,
- 'change_freq = %S', $changeFreq,
- 'priority = %l', is_null($priority)?'NULL':$priority
+ try {
+
+ $loc = trim($loc);
+ $checksum = crc32($loc);
+
+ /* get the host name from url */
+ preg_match('@^(?:https?:\/\/)?((?:www\.)?[^\/]+)@i', $loc, $matches);
+
+ $host = $matches[1];
+
+ $row = $db->select_1(
+ 'select * from sitemap where loc = %s',
+ $loc
);
-
+
+ /* new location */
+ if(is_null($row)) {
+ $db->insert(
+ 'sitemap',
+ 'checksum = \'%l\'::bigint', $checksum,
+ 'host = %s', $host,
+ 'loc = %s', $loc,
+ 'last_mod = %T', $lastMod,
+ 'change_freq = %S', $changeFreq,
+ 'priority = %l', is_null($priority)?'NULL':$priority
+ );
+
+ /* update as details have changed */
+ } elseif($row->t_last_mod != $lastMod || $row->s_change_freq != $changeFreq || $row->s_priority != $priority) {
+ $db->update_1(
+ 'sitemap',
+ 'loc = %s', $loc,
+ 'last_mod = %T', $lastMod,
+ 'change_freq = %S', $changeFreq,
+ 'priority = %l', is_null($priority)?'NULL':$priority
+ );
+
+ }
+
+ /* nulling used variables (big sitemaps need every scrap of memory!) */
+ $row = null;
+ $host = null;
+ $checksum = null;
+ $matches = null;
+
+ } catch (Exception $e) {
+ Atsumi::error__listen($e);
}
-
- /* nulling used variables (big sitemaps need every scrap of memory!) */
- $row = null;
- $host = null;
- $checksum = null;
- $matches = null;
}
/* gets total number of urls for host */
@@ -63,15 +72,21 @@ static public function getUrlCount(&$db, $host, $tablename = 'sitemap') {
/* writes sitemap files to disk */
static public function writeXml(&$db, $host, $xmlFilePath, $xmlUrlRoot, $maxUrlsPerSitemap = null, $compress = true, $tablename = 'sitemap') {
+ if (!is_dir($xmlFilePath) && !mkdir($xmlFilePath, 0777, true)) {
+ throw new sitemap_WriteException("Failed to create neccessary folders to save XML site map: ".$xmlFilePath);
+ }
+
/* clean up old sitemap files */
foreach(glob($xmlFilePath."sitemap*.xml*") as $filename)
unlink($filename);
if(is_null($maxUrlsPerSitemap)) $maxUrlsPerSitemap = self::MAX_URLS_PER_SITEMAP;
+
/* how many items required */
$count = self::getUrlCount($db, $host, $tablename);
+
/* array of sitemap files */
$siteMapArr = array();
diff --git a/classes/utility/calendar/atsumi_Date.php b/classes/utility/calendar/atsumi_Date.php
new file mode 100644
index 0000000..1c94a2c
--- /dev/null
+++ b/classes/utility/calendar/atsumi_Date.php
@@ -0,0 +1,61 @@
+year = (int) $year;
+ $this->month = (int) $month;
+ $this->day = (int) $day;
+ }
+
+ public function __toString() {
+ return $this->getYmd();
+ }
+
+ public function getYear () { return $this->year; }
+ public function getMonth () { return $this->month; }
+ public function getDay () { return $this->day; }
+
+
+ public function adjust ($year = 0, $month = 0, $day = 0) {
+ $newDate = self::fromTimestamp(mktime (0,0,0, $this->month+$month, $this->day+$day, $this->year+$year));
+ $this->year = $newDate->getYear();
+ $this->month = $newDate->getMonth();
+ $this->day = $newDate->getDay();
+ }
+
+ public function getTimestampDayStart () {
+ return mktime (0,0,0, $this->month, $this->day, $this->year);
+ }
+ public function getTimestampDayEnd () {
+ return mktime (0,0,0, $this->month, $this->day + 1, $this->year);
+ }
+ public function getYmd () {
+ return sprintf ('%04d-%02d-%02d', $this->year, $this->month, $this->day);
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php
new file mode 100644
index 0000000..dbceab8
--- /dev/null
+++ b/classes/utility/calendar/atsumi_DateTime.php
@@ -0,0 +1,36 @@
+timestamp = (int) $timestamp;
+ }
+
+ public function __toString() {
+ return (String) $this->timestamp;
+ }
+
+ public function format($formatString = null) {
+ if (is_null($formatString)) $formatString = self::FORMAT_FRIENDLY;
+ return date($formatString, $this->timestamp);
+ }
+ static public function formatFromTimestamp ($timestamp, $formatString = null) {
+ $d = new self($timestamp);
+ return $d->format($formatString);
+ }
+
+ public function output ($outputType) {
+ return (String) $this->timestamp;
+ }
+}
+?>
\ No newline at end of file
diff --git a/classes/utility/calendar/atsumi_Interval.php b/classes/utility/calendar/atsumi_Interval.php
new file mode 100644
index 0000000..8327022
--- /dev/null
+++ b/classes/utility/calendar/atsumi_Interval.php
@@ -0,0 +1,127 @@
+newInstanceArgs($intervalMatches);
+ return $intervalInstance;
+
+
+ } else
+ throw new Exception ('Invalid interval format');
+
+ }
+
+ public function __construct ($seconds, $minutes = 0, $hours = 0, $days = 0, $years = 0) {
+
+ // TODO: check date is valid
+ $this->seconds = floatval(
+ floatval($seconds) +
+ (intval($minutes) * self::DURATION_MINUTE) +
+ (intval($hours) * self::DURATION_HOUR) +
+ (intval($days) * self::DURATION_DAY) +
+ (intval($years) * self::DURATION_YEAR)
+ );
+ }
+
+ public function add (self $interval) {
+ $this->seconds += $interval->inSeconds();
+ }
+
+ public function __toString() {
+ return strval($this->inSeconds());
+ }
+
+ public function inSeconds() {
+ return floatval($this->seconds);
+ }
+
+ public function inMinutes() {
+ return floatval($this->seconds / self::DURATION_MINUTE);
+ }
+
+ public function inHours() {
+ return floatval($this->seconds / self::DURATION_HOUR);
+ }
+
+ public function inDays() {
+ return floatval($this->seconds / self::DURATION_DAY);
+ }
+
+ public function inYears() {
+ return floatval($this->seconds / self::DURATION_YEAR);
+ }
+ public function getFormatBreakdown() {
+
+ $remainder = $this->inSeconds();
+
+ $years = floor($remainder / self::DURATION_YEAR);
+ $remainder -= $years * self::DURATION_YEAR;
+
+ $days = floor($remainder / self::DURATION_DAY);
+ $remainder -= $days * self::DURATION_DAY;
+
+ $hours = floor($remainder / self::DURATION_HOUR);
+ $remainder -= $hours * self::DURATION_HOUR;
+
+ $minutes = floor($remainder / self::DURATION_MINUTE);
+ $remainder -= $minutes * self::DURATION_MINUTE;
+
+ $seconds = floor($remainder);
+
+ return array(
+ 'years' => $years,
+ 'days' => $days,
+ 'hours' => $hours,
+ 'minutes' => $minutes,
+ 'seconds' => $seconds,
+ );
+ }
+
+ public function format($compact = false) {
+
+ $formatComponents = $this->getFormatBreakdown();
+
+ return sf('%s%s%s:%s:%s',
+ $formatComponents['years']?sf('%sy ',$formatComponents['years']):'',
+ $formatComponents['days']||$formatComponents['days']?sf('%sd ',$formatComponents['days']):'',
+ sprintf('%02d', $formatComponents['hours']),
+ sprintf('%02d', $formatComponents['minutes']),
+ sprintf('%02d', $formatComponents['seconds'])
+
+ );
+
+ }
+
+
+}
+?>
\ No newline at end of file
diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php
new file mode 100644
index 0000000..afdd478
--- /dev/null
+++ b/classes/utility/http/atsumi_Http.php
@@ -0,0 +1,132 @@
+ $val)
+ curl_setopt($ch, $opt, $val);
+
+
+
+ $response = curl_exec($ch);
+
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ if ($httpCode == 0)
+ throw new atsumi_HttpException (
+ 'CURL error: #'.curl_errno($ch),
+ curl_errno($ch)
+ );
+
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $header = substr($response, 0, $headerSize);
+ $body = substr($response, $headerSize);
+
+ $httpRequestHeaders = curl_getinfo($ch, CURLINFO_HEADER_OUT );
+
+ //close connection
+ curl_close($ch);
+
+
+ return new atsumi_HttpResponse(
+ $httpCode,
+ $header,
+ $body
+ );
+
+ case self::POST_METHOD_PECL:
+
+ return http_post_fields($url, $fields);
+
+ }
+ }
+
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/classes/utility/http/atsumi_HttpException.php b/classes/utility/http/atsumi_HttpException.php
new file mode 100644
index 0000000..ba72037
--- /dev/null
+++ b/classes/utility/http/atsumi_HttpException.php
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/classes/utility/http/atsumi_HttpResponse.php b/classes/utility/http/atsumi_HttpResponse.php
new file mode 100644
index 0000000..4652f48
--- /dev/null
+++ b/classes/utility/http/atsumi_HttpResponse.php
@@ -0,0 +1,35 @@
+httpCode = $httpCode;
+ $this->header = $header;
+ $this->body = $body;
+
+ }
+
+ public function getHttpCode() { return $this->httpCode; }
+ public function getBody() { return $this->body; }
+ public function getHeader() { return $this->header; }
+ public function getSuccessResponse() {
+ return $this->httpCode >= 200 && $this->httpCode < 300;
+ }
+
+ public function getSummaryString () {
+
+ return sf('HTTP CODE: %s, SUCCESS: %s, BODY:%s',
+ $this->httpCode,
+ $this->getSuccessResponse()?'YES':'NO',
+ empty($this->body)?'EMPTY':sf('"%s"', $this->body)
+ );
+
+ }
+
+
+}
+?>
\ No newline at end of file
diff --git a/classes/utility/math/atsumi_Math.php b/classes/utility/math/atsumi_Math.php
new file mode 100644
index 0000000..9094896
--- /dev/null
+++ b/classes/utility/math/atsumi_Math.php
@@ -0,0 +1,21 @@
+ $intMax) $out = $in - $intMax * 2 - 2;
+ else $out = $in;
+
+ return $out;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/classes/utility/security/atsumI_Security.php b/classes/utility/security/atsumI_Security.php
new file mode 100644
index 0000000..607a4d3
--- /dev/null
+++ b/classes/utility/security/atsumI_Security.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/classes/validators/exceptions/ValidationException.php b/classes/validators/exceptions/ValidationException.php
index 729e7dd..262eafa 100644
--- a/classes/validators/exceptions/ValidationException.php
+++ b/classes/validators/exceptions/ValidationException.php
@@ -2,7 +2,4 @@
class ValidationException extends Exception {
- public function __construct($message = null, $code = null) {
- parent::__construct('Validation Failed: '.$message, $code);
- }
}
\ No newline at end of file
diff --git a/classes/validators/exceptions/ValidationIncorrectFileTypeException.php b/classes/validators/exceptions/ValidationIncorrectFileTypeException.php
new file mode 100644
index 0000000..3a7fa62
--- /dev/null
+++ b/classes/validators/exceptions/ValidationIncorrectFileTypeException.php
@@ -0,0 +1,5 @@
+
+?>
\ No newline at end of file
diff --git a/classes/validators/validate_EmailAddress.php b/classes/validators/validate_EmailAddress.php
index da30925..4d1f20e 100644
--- a/classes/validators/validate_EmailAddress.php
+++ b/classes/validators/validate_EmailAddress.php
@@ -20,7 +20,8 @@ public function __construct($testDomain = true, $arrayIndex = null) {
public function validate($data) {
if(is_array($data)) $data = $data[$this->arrayIndex];
- if(empty($data) || $this->validEmailAddress($data)) {
+ if (is_null($data) || !strlen($data)) return true;
+ if($this->validEmailAddress($data)) {
if($this->testDomain) {
if($this->checkDomainExists($data)) {
return true;
diff --git a/classes/validators/validate_FileType.php b/classes/validators/validate_FileType.php
index 2c11b88..5f253b0 100644
--- a/classes/validators/validate_FileType.php
+++ b/classes/validators/validate_FileType.php
@@ -108,7 +108,7 @@ public function validate($data) {
$incorrectExtensionArr = $this->getExtensionFromMime($data['type']);
}
- throw new Exception(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf('(not a %s)', implode('/', $incorrectExtensionArr)):''));
+ throw new ValidationIncorrectFileTypeException(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf(' (not a %s)', implode('/', $incorrectExtensionArr)):''));
}
diff --git a/classes/validators/validate_IpAddress.php b/classes/validators/validate_IpAddress.php
new file mode 100644
index 0000000..ef335a5
--- /dev/null
+++ b/classes/validators/validate_IpAddress.php
@@ -0,0 +1,22 @@
+
diff --git a/classes/validators/validate_MaxChars.php b/classes/validators/validate_MaxChars.php
index 3d610e7..3bac03e 100644
--- a/classes/validators/validate_MaxChars.php
+++ b/classes/validators/validate_MaxChars.php
@@ -9,23 +9,27 @@
class validate_MaxChars extends validate_AbstractValidator {
private $maxChars = 0;
+ private $encoding = 'UTF-8';
- public function __construct($max) {
+ public function __construct($max, $encoding = null) {
$this->maxChars = $max;
+
+ if (!is_null($encoding))
+ $this->encoding = $encoding;
}
public function validate($data) {
if(is_array($data))$data = $data[0];
$data = str_replace("\n", " ", str_replace("\r", "", $data));
- if(empty($data) || strlen($data) <= $this->maxChars)
+ if(empty($data) || mb_strlen($data, $this->encoding) <= $this->maxChars)
return true;
else
- throw new Exception(sf("You must enter less than %s characters",
+ throw new Exception(sf("You must enter %s characters or less",
$this->maxChars
));
}
}
-?>
+?>
\ No newline at end of file
diff --git a/classes/validators/validate_MinAge.php b/classes/validators/validate_MinAge.php
index 3ec4fea..97e8087 100644
--- a/classes/validators/validate_MinAge.php
+++ b/classes/validators/validate_MinAge.php
@@ -2,7 +2,7 @@
class validate_MinAge extends validate_AbstractValidator {
- protected $maxAge;
+ protected $minAge;
public function __construct($minAge) {
$this->minAge = $minAge;
diff --git a/classes/validators/validate_MinChars.php b/classes/validators/validate_MinChars.php
index 2be6a73..6c32261 100644
--- a/classes/validators/validate_MinChars.php
+++ b/classes/validators/validate_MinChars.php
@@ -19,7 +19,7 @@ public function validate($data) {
if(empty($data) || strlen($data) >= $this->minChars)
return true;
- else throw new Exception(sf("You must enter more than %s characters",
+ else throw new Exception(sf("You must enter %s or more characters",
$this->minChars
));
diff --git a/classes/validators/validate_Recaptcha.php b/classes/validators/validate_Recaptcha.php
new file mode 100644
index 0000000..8374b37
--- /dev/null
+++ b/classes/validators/validate_Recaptcha.php
@@ -0,0 +1,173 @@
+privateKey = $privateKey;
+ }
+ public function getUserIp () {
+
+ // cloudflare
+ if (array_key_exists('HTTP_CF_CONNECTING_IP', $_SERVER))
+ return $_SERVER['HTTP_CF_CONNECTING_IP'];
+
+ // proxy
+ elseif (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
+ return $_SERVER['HTTP_X_FORWARDED_FOR'];
+
+ // direct IP
+ elseif (array_key_exists('REMOTE_ADDR', $_SERVER))
+ return $_SERVER['REMOTE_ADDR'];
+
+ else return false;
+
+ }
+ public function validate($data) {
+ // We don't care about the data variable, check for...
+ if(!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) {
+ throw new Exception('Missing required reCAPTCHA field');
+ }
+
+ $answer = $this->recaptcha_check_answer($this->privateKey,
+ $this->getUserIp(),
+ $_POST["recaptcha_challenge_field"],
+ $_POST["recaptcha_response_field"]
+ );
+ if($answer->is_valid) {
+ return true;
+ }
+ throw new Exception('Invalid CAPTCHA answer');
+ }
+
+ /*
+ * Hacked up and class-ified functions from the recaptchalib.php written by
+ * Mike Crawford
+ * Ben Maurer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+ /**
+ * The reCAPTCHA server URL's
+ */
+ const RECAPTCHA_VERIFY_SERVER = 'www.google.com';
+
+ /**
+ * Encodes the given data into a query string format
+ * @param $data - array of string elements to be encoded
+ * @return string - encoded request
+ */
+ function _recaptcha_qsencode ($data) {
+ $req = "";
+ foreach ( $data as $key => $value )
+ $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
+
+ // Cut the last '&'
+ $req=substr($req,0,strlen($req)-1);
+ return $req;
+ }
+
+
+ /**
+ * Submits an HTTP POST to a reCAPTCHA server
+ * @param string $host
+ * @param string $path
+ * @param array $data
+ * @param int port
+ * @return array response
+ */
+ function _recaptcha_http_post($host, $path, $data, $port = 80) {
+ $req = $this->_recaptcha_qsencode ($data);
+ $http_request = "POST $path HTTP/1.0\r\n";
+ $http_request .= "Host: $host\r\n";
+ $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
+ $http_request .= "Content-Length: " . strlen($req) . "\r\n";
+ $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
+ $http_request .= "\r\n";
+ $http_request .= $req;
+
+ $response = '';
+ if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
+ die ('Could not open socket');
+ }
+
+ fwrite($fs, $http_request);
+
+ while ( !feof($fs) )
+ $response .= fgets($fs, 1160); // One TCP-IP packet
+ fclose($fs);
+ $response = explode("\r\n\r\n", $response, 2);
+
+ return $response;
+ }
+
+
+ /**
+ * Calls an HTTP POST function to verify if the user's guess was correct
+ * @param string $privkey
+ * @param string $remoteip
+ * @param string $challenge
+ * @param string $response
+ * @param array $extra_params an array of extra variables to post to the server
+ * @return ReCaptchaResponse
+ */
+ function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) {
+ if ($privkey == null || $privkey == '') {
+ die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create");
+ }
+ if ($remoteip == null || $remoteip == '') {
+ die ("For security reasons, you must pass the remote ip to reCAPTCHA");
+ }
+
+ //discard spam submissions
+ if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
+ $recaptcha_response = new ReCaptchaResponse();
+ $recaptcha_response->is_valid = false;
+ $recaptcha_response->error = 'incorrect-captcha-sol';
+ return $recaptcha_response;
+ }
+ $response = $this->_recaptcha_http_post (self::RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
+ array (
+ 'privatekey' => $privkey,
+ 'remoteip' => $remoteip,
+ 'challenge' => $challenge,
+ 'response' => $response
+ ) + $extra_params
+ );
+
+ $answers = explode ("\n", $response [1]);
+ $recaptcha_response = new ReCaptchaResponse();
+ if (trim ($answers [0]) == 'true') {
+ $recaptcha_response->is_valid = true;
+ } else {
+ $recaptcha_response->is_valid = false;
+ $recaptcha_response->error = $answers [1];
+ }
+ return $recaptcha_response;
+ }
+}
+
+
+/**
+ * A ReCaptchaResponse is returned from recaptcha_check_answer()
+ */
+class ReCaptchaResponse {
+ var $is_valid;
+ var $error;
+}
+?>
diff --git a/classes/validators/validate_Required.php b/classes/validators/validate_Required.php
index 4ecc0dc..933396e 100644
--- a/classes/validators/validate_Required.php
+++ b/classes/validators/validate_Required.php
@@ -18,7 +18,7 @@ public function isRequired() {
return $this->required;
}
public function validate($data) {
- if(!$this->required ||(!is_null($data) && strval($data) != "" && !is_array($data)))
+ if(!$this->required || (!is_array($data) && !is_null($data) && trim(strval($data)) !== ""))
return true;
elseif(is_array($data)) {
$empty = true;
diff --git a/classes/validators/validation_Handler.php b/classes/validators/validation_Handler.php
new file mode 100644
index 0000000..91a49e1
--- /dev/null
+++ b/classes/validators/validation_Handler.php
@@ -0,0 +1,31 @@
+validate($value);
+ } catch(Exception $e) {
+ $errors[] = $e->getMessage();
+ $validates = false;
+ }
+ }
+
+ return array (
+ "validates" => $validates,
+ "errors" => $errors
+ );
+ }
+
+
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php
index c23c87b..3c28c1d 100644
--- a/classes/widgets/form/elements/widget_AbstractElement.php
+++ b/classes/widgets/form/elements/widget_AbstractElement.php
@@ -1,65 +1,105 @@
outputGeneric(),
+
+ return array_merge( $this->outputGeneric(),
$this->outputSpecific());
}
protected function outputGeneric() {
-
+
return array( "name" => $this->getName(),
"value" => $this->getValue(),
"validation" => $this->getValidationData()
- );
+ );
}
*/
-
+
protected function preRender() { }
protected function postRender() { }
-
+
static public function makeInputSafe($in) {
$in = str_replace("'", "'", $in);
$in = str_replace('"', '"', $in);
return $in;
}
-
- public function render() {
- $out = $this->preRender();
- $out .= sfl(' %s%s%s ',
- $this->style ? " " . $this->style : "",
- ($this->submitted && !$this->validates) ? " error" : "",
- $this->name,
- $this->renderErrors(),
- ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''),
- $this->renderElement());
- $out .= $this->postRender();
- return $out;
+
+ public function render($options = array()) {
+ // If elementOnly is specified, only the form element itself is returned
+ if (isset($options['elementOnly']) && $options['elementOnly']) {
+ $out = $this->renderElement();
+
+ } else {
+ $out = $this->preRender();
+ $out .= sfl(' ',
+ $this->style ? " " . $this->style : "",
+ $this->cssClass ? " " . $this->cssClass : "",
+ ($this->submitted && !$this->validates) ? " error" : "",
+ $this->name,
+ $this->cssStyle ? " style='" . $this->cssStyle . "'": ""
+ );
+
+ try {
+ $out .= sf('%s%s %s ',
+ $this->renderErrors(),
+ (($this->label != '' || $this->getRequired()) &&
+ (!array_key_exists('label', $options) || $options['label'] !== false) ?
+ $this->renderLabel() : ''),
+ $this->renderElement());
+ } catch (Exception $e) {
+
+ // if in debug mode display exception details
+ if (atsumi_Debug::getActive())
+ $out .= sfl('Element Exception "%s": %s #%s', $e->getMessage(), $e->getFile(), $e->getLine());
+
+ // fire error listeners
+ Atsumi::error__listen($e);
+ }
+ $out .= ' ';
+
+ $out .= $this->postRender();
+ }
+
+ return $out;
+
}
-
- protected function renderErrors() {
+
+ public function hasErrors () {
+ return count($this->errors)?true:false;
+ }
+ public function renderErrors() {
if(!count($this->errors) || !$this->submitted || $this->forceDefault) return;
$div = " ";
$div .= " ";
@@ -69,20 +109,20 @@ protected function renderErrors() {
$div .= " ";
$div .= " ";
return $div;
-
+
}
-
- protected function renderLabel() {
+
+ protected function renderLabel() {
return sf(" ",
$this->getName(),
$this->label,
($this->getRequired() && !$this->getValidates()) ? $this->goAsterisks() : "");
}
-
+
protected function goAsterisks() {
- return " *";
- }
-
+ return " *";
+ }
+
public function validate() {
if(is_null($this->validators)) return true;
@@ -95,12 +135,12 @@ public function validate() {
foreach($this->validators as $validator) {
if($validator instanceof validate_Required && $validator->isRequired()) $this->required = true;
-
+
$elementValue =($this->forceDefault) ? null : $this->getValue();
-
+
if(is_null($elementValue) && !is_null($this->getDefault()))
$elementValue = $this->getDefault();
-
+
try {
$validator->validate($elementValue);
} catch(Exception $e) {
@@ -110,72 +150,94 @@ public function validate() {
}
}
protected function getValidationData() {
-
- if($this->submitted && !$this->forceDefault)
+
+ if($this->submitted && !$this->forceDefault)
$validateArr = array( "validates" => $this->validates,
"errors" => $this->errors
);
else $validateArr = array( "validates" => $this->validates,
"errors" => array());
-
+
return $validateArr;
}
function setSubmitted($in) {
$this->submitted = $in;
}
+
public function setValidators($in) {
$this->validators = $in;
}
public function setName($in) {
if(!is_string($in)) throw new Exception("Name must be of type String");
- $this->name = $in;
+ $this->name = $in;
}
+
public function setLabel($in) {
- $this->label = $in;
+ $this->label = $in;
}
+
public function setError(Exception $e) {
- $this->errors[] = $e->getMessage();
+ $this->errors[] = $e->getMessage();
}
- public function setStyle($in) {
- $this->style = $in;
+ public function setErrors($errors) {
+ $this->errors = $errors;
}
-
+
public function setValue($input, $files = array()) {
- $this->value = isset($input[$this->name]) ? $input[$this->name] : null;
+ $this->value = isset($input[$this->name]) ? $input[$this->name] : null;
}
-
+
public function setDefault($in) {
- $this->defaultValue = $in;
+ $this->defaultValue = $in;
+ }
+
+ public function setCssClass($in) {
+ $this->cssClass = $in;
+ }
+
+ public function setCssStyle($in) {
+ $this->cssStyle = $in;
+ }
+
+ public function setTabindex($in) {
+ $this->tabindex = (int) $in;
}
public function setForceDefault($in) {
if(!is_bool($in)) throw new Exception("Force Default must be of type Bool");
- $this->forceDefault = $in;
+ $this->forceDefault = $in;
}
-
+
public function getName() {
- return $this->name;
+ return $this->name;
}
public function getValue() {
- return(is_null($this->value) || $this->getForceDefault()) ?
- $this->defaultValue :
- $this->value;
+ return(is_null($this->value) || $this->getForceDefault()) ?
+ $this->defaultValue :
+ $this->value;
}
public function getValidates() {
- return $this->validates;
+ return $this->validates;
}
public function getDefault() {
- return $this->defaultValue;
+ return $this->defaultValue;
}
public function getForceDefault() {
- return $this->forceDefault;
+ return $this->forceDefault;
}
public function getRequired() {
- return $this->required;
+ return $this->required;
}
-
+
+ /**
+ * @deprecated
+ */
+ public function setStyle($in) {
+ $this->style = $in;
+ }
+
}
diff --git a/classes/widgets/form/elements/widget_AgeRangeElement.php b/classes/widgets/form/elements/widget_AgeRangeElement.php
index 8c1c3cf..46aa769 100644
--- a/classes/widgets/form/elements/widget_AgeRangeElement.php
+++ b/classes/widgets/form/elements/widget_AgeRangeElement.php
@@ -16,10 +16,10 @@ function renderElement() {
$date = $this->getValue();
-
- $minValue = is_array($date) && array_key_exists(0,$date)?$date[0]:null;
- $maxValue = is_array($date) && array_key_exists(1,$date)?$date[1]:null;
-
+
+ $minValue = is_array($date) && array_key_exists('min',$date)?$date['min']:null;
+ $maxValue = is_array($date) && array_key_exists('max',$date)?$date['max']:null;
+
$minOptions = sfl(" ");
@@ -45,12 +45,12 @@ function renderElement() {
}
- $out = sfl(" ",
- $this->getName(), $this->getName(), $minOptions
+ $out = sfl(" ",
+ $this->getName(), $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $minOptions
);
- $out .= sfl(" %s ",
- $this->separator, $this->getName(), $this->getName(), $maxOptions
+ $out .= sfl(" %s ",
+ $this->separator, $this->getName(), $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $maxOptions
);
return $out;
diff --git a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php
index 2a9712c..2509d07 100644
--- a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php
+++ b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php
@@ -22,12 +22,20 @@ public function __construct($args) {
}
function setValue($input, $files = array()) {
- if(empty($input[$this->name])) $input[$this->name] = $this->default;
+
+ if (!isset($input[$this->name]))
+ $input[$this->name] = array();
+
+ if(empty($input[$this->name]))
+ $input[$this->name] = $this->default;
+
// creates an array of ids holding boolean values
- foreach($this->options as $option => $name) {
- if(!isset($input[$this->name][$option]) || !$input[$this->name][$option]) $input[$this->name][$option] = false;
- else $input[$this->name][$option] = true;
- }
+ if (count($input[$this->name]))
+ foreach($this->options as $option => $name) {
+ if(!isset($input[$this->name][$option]) || !$input[$this->name][$option]) $input[$this->name][$option] = false;
+ else $input[$this->name][$option] = true;
+ }
+
$this->value = $input[$this->name];
}
@@ -36,19 +44,21 @@ function renderElement() {
$valueArr = $this->getValue();
//sort the options into 3 cols
- if($this->sort) {
+ if($this->sort)
asort($this->options);
-
- }
+
+
foreach($this->options as $value => $option) {
- $out.=(sf(" %s ",
+ $out.=(sf(" %s ",
$this->getName(),$value,
(in_array($value, $valueArr) && $valueArr[$value]) ? "checked='checked'" : "",
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),$value,
$this->getName(),$value,
$option, $this->delimiter
));
}
+
return " ".$out."
";
}
diff --git a/classes/widgets/form/elements/widget_CheckBoxElement.php b/classes/widgets/form/elements/widget_CheckBoxElement.php
index 623a01c..38fe484 100644
--- a/classes/widgets/form/elements/widget_CheckBoxElement.php
+++ b/classes/widgets/form/elements/widget_CheckBoxElement.php
@@ -2,9 +2,13 @@
class widget_CheckBoxElement extends widget_AbstractElement {
+ protected $onClick;
public function __construct($args) {
+ if (array_key_exists('onClick', $args) && strlen($args['onClick']))
+ $this->onClick = $args['onClick'];
+
}
public function getValue() {
@@ -15,10 +19,12 @@ public function setValue($input, $files = null) {
$this->value =(isset($input[$this->name]) && $input[$this->name] == 'on') ? true : false;
}
function renderElement() {
- return(sf(" ",
+ return(sf(" ",
$this->getName(),
$this->getValue() ? "checked='checked'" : "",
- $this->getName()
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(),
+ !is_null($this->onClick) && strlen($this->onClick)?sf(' onClick="%s"', $this->onClick):''
));
}
}
diff --git a/classes/widgets/form/elements/widget_DateElement.php b/classes/widgets/form/elements/widget_DateElement.php
index 5bb03ff..c75ebbe 100644
--- a/classes/widgets/form/elements/widget_DateElement.php
+++ b/classes/widgets/form/elements/widget_DateElement.php
@@ -51,20 +51,23 @@ function renderElement() {
- $out = sfl(" ",
+ $out = sfl(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
$dayOptions
);
- $out .= sfl(" ",
+ $out .= sfl(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
$monthOptions
);
- $out .= sfl(" ",
+ $out .= sfl(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
$yearOptions
);
diff --git a/classes/widgets/form/elements/widget_FileElement.php b/classes/widgets/form/elements/widget_FileElement.php
index c2ca34a..50b7f99 100644
--- a/classes/widgets/form/elements/widget_FileElement.php
+++ b/classes/widgets/form/elements/widget_FileElement.php
@@ -19,8 +19,8 @@ public function setValue($input = null, $files = null) {
if(is_array($uploadedFile) && $uploadedFile['error'] == 0) {
$this->fileToken = md5(microtime());
- copy($uploadedFile['tmp_name'], "/tmp/".$this->fileToken);
- $uploadedFile['tmp_name'] = "/tmp/".$this->fileToken;
+ copy($uploadedFile['tmp_name'], ini_get('upload_tmp_dir')."/".$this->fileToken);
+ $uploadedFile['tmp_name'] = ini_get('upload_tmp_dir')."/".$this->fileToken;
$this->originalName = $uploadedFile['name'];
$_SESSION[$this->fileToken] = $uploadedFile;
$this->value = $uploadedFile;
@@ -32,6 +32,12 @@ public function setValue($input = null, $files = null) {
}
}
+ public function clear() {
+ $this->fileToken = null;
+ $this->originalName = '';
+ $this->value = null;
+ }
+
protected function outputGeneric() {
return array( "name" => $this->getName(),
@@ -55,8 +61,9 @@ function renderElement() {
$this->getName()
));
else
- return(sf(" ",
+ return(sf(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName()
));
}
diff --git a/classes/widgets/form/elements/widget_HiddenElement.php b/classes/widgets/form/elements/widget_HiddenElement.php
index 7fe38f6..4bff36b 100644
--- a/classes/widgets/form/elements/widget_HiddenElement.php
+++ b/classes/widgets/form/elements/widget_HiddenElement.php
@@ -2,14 +2,25 @@
class widget_HiddenElement extends widget_AbstractElement {
+ protected $attributes = array();
+
public function __construct($args) {
+ if (array_key_exists('attributes', $args) && is_array($args['attributes']))
+ $this->attributes = $args['attributes'];
}
function renderElement() {
- return(sf(" ",
- $this->getName(),
- parent::makeInputSafe($this->getValue()),
- $this->getName()
+
+ $attributesHtml = '';
+ if (count($this->attributes))
+ foreach ($this->attributes as $key => $val)
+ $attributesHtml.= sf(' %s="%s" ', $key, $val);
+
+ return(sf(" ",
+ $this->getName(),
+ parent::makeInputSafe($this->getValue()),
+ $this->getName(),
+ $attributesHtml
));
}
}
diff --git a/classes/widgets/form/elements/widget_HtmlElement.php b/classes/widgets/form/elements/widget_HtmlElement.php
index c4d2870..6d05bf3 100644
--- a/classes/widgets/form/elements/widget_HtmlElement.php
+++ b/classes/widgets/form/elements/widget_HtmlElement.php
@@ -12,7 +12,7 @@ public function __construct($args) {
function renderElement() {
}
- public function render() {
+ public function render($options = array()) {
$out = $this->preRender();
$out .= sf("%s", $this->html);
$out .= $this->postRender();
diff --git a/classes/widgets/form/elements/widget_ImagePickerElement.php b/classes/widgets/form/elements/widget_ImagePickerElement.php
new file mode 100644
index 0000000..04608ca
--- /dev/null
+++ b/classes/widgets/form/elements/widget_ImagePickerElement.php
@@ -0,0 +1,80 @@
+options = $args['options'];
+ if(array_key_exists('blank', $args))
+ $this->blankMessage = $args['blank'];
+
+ if(array_key_exists('width', $args))
+ $this->width = $args['width'];
+
+ if(array_key_exists('height', $args))
+ $this->height = $args['height'];
+ }
+ function renderThumbnail ($value, $imageUrl, $selected) {
+
+ // TODO: Would be nice to add option for multi select
+ // would convert to checkbox's rather than radio's
+
+ return sfl('
+
+
+ ',
+ $this->width,
+ $this->getName(),
+ $value,
+ $imageUrl,
+ $this->width,
+ $this->height,
+ $this->getName(),
+ $value,
+ $this->getName(),
+ $value,
+ $selected?'checked="checked"':'');
+
+ }
+ function renderElement() {
+ $htmlOut = "";
+ $elementValue = $this->getValue();
+
+ foreach($this->options as $value => $imageUrl) {
+
+ $htmlOut .= $this->renderThumbnail(
+ $value,
+ $imageUrl,
+ strval($elementValue) == strval($value)
+ );
+
+ }
+
+ return( sfl(" %s ",
+ $this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(),
+ $htmlOut
+
+ )
+ );
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/classes/widgets/form/elements/widget_NameElement.php b/classes/widgets/form/elements/widget_NameElement.php
index 3bc8f23..77facba 100644
--- a/classes/widgets/form/elements/widget_NameElement.php
+++ b/classes/widgets/form/elements/widget_NameElement.php
@@ -42,16 +42,16 @@ function renderElement() {
}
- $out = sfl(" ",
- $this->getName(), $this->getName(), $titleOptions
+ $out = sfl(" ",
+ $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $titleOptions
);
- $out .= sfl(" ",
- $this->getName(), parent::makeInputSafe($name['firstName']), $this->getName()
+ $out .= sfl(" ",
+ $this->getName(), parent::makeInputSafe($name['firstName']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName()
);
- $out .= sfl(" ",
- $this->getName(), parent::makeInputSafe($name['lastName']), $this->getName()
+ $out .= sfl(" ",
+ $this->getName(), parent::makeInputSafe($name['lastName']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName()
);
return $out;
diff --git a/classes/widgets/form/elements/widget_RadioElement.php b/classes/widgets/form/elements/widget_RadioElement.php
index b89e016..736ad41 100644
--- a/classes/widgets/form/elements/widget_RadioElement.php
+++ b/classes/widgets/form/elements/widget_RadioElement.php
@@ -2,18 +2,33 @@
class widget_RadioElement extends widget_AbstractElement {
private $options = array();
+ private $onChange = null;
public function __construct($args) {
$this->options = $args['options'];
+ $this->onChange = isset($args['onChange'])?$args['onChange']:null;
}
function renderElement() {
$html = "";
$elementValue = $this->getValue();
foreach($this->options as $value => $option) {
- if(strval($elementValue) == strval($value))
- $html .= sfl(' ', $this->getName(), $value, $this->getName(),$value, $this->getName(), $value, $option);
- else
- $html .= sfl(' ', $this->getName(), $value, $this->getName(),$value, $this->getName(), $value, $option);
+
+ $html .= sfl(
+ ' '.
+ ' '.
+ ' '.
+ ' ',
+ $this->getName(),
+ $value,
+ $this->onChange?sf(' onchange="%s"', $this->onChange):'',
+ strval($elementValue) == strval($value) ? sf('checked="checked"') : '',
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(),
+ $value,
+ $this->getName(),
+ $value,
+ $option
+ );
}
return sf(" %s ",$html);
diff --git a/classes/widgets/form/elements/widget_RecaptchaElement.php b/classes/widgets/form/elements/widget_RecaptchaElement.php
new file mode 100644
index 0000000..718ab49
--- /dev/null
+++ b/classes/widgets/form/elements/widget_RecaptchaElement.php
@@ -0,0 +1,24 @@
+publicKey = $args['apiKey'];
+ }
+ function renderElement() {
+ $output = sf('
+
+ ', $this->publicKey, $this->publicKey);
+ return $output;
+ }
+}
+?>
diff --git a/classes/widgets/form/elements/widget_SelectElement.php b/classes/widgets/form/elements/widget_SelectElement.php
index 6dcda78..167ddbf 100644
--- a/classes/widgets/form/elements/widget_SelectElement.php
+++ b/classes/widgets/form/elements/widget_SelectElement.php
@@ -10,21 +10,21 @@ public function __construct($args) {
$this->blankMessage = $args['blank'];
}
function renderElement() {
- $opionHtml = "";
+ $optionHtml = "";
$elementValue = $this->getValue();
if(!$this->defaultValue && $this->blankMessage)
- $opionHtml .= sfl(" ", $this->blankMessage);
+ $optionHtml .= sfl(" ", $this->blankMessage);
elseif($this->defaultValue) {
// TODO: put this in a func...
foreach($this->defaultValue as $value => $option) {
if(strval($elementValue) == strval($value))
- $opionHtml .= sfl(" ",
+ $optionHtml .= sfl(" ",
$value,
$option
);
else
- $opionHtml .= sfl(" ",
+ $optionHtml .= sfl(" ",
$value,
$option
);
@@ -33,21 +33,22 @@ function renderElement() {
}
foreach($this->options as $value => $option) {
if(strval($elementValue) == strval($value))
- $opionHtml .= sfl(" ",
+ $optionHtml .= sfl(" ",
$value,
$option
);
else
- $opionHtml .= sfl(" ",
+ $optionHtml .= sfl(" ",
$value,
$option
);
}
- return( sfl(" ",
+ return( sfl(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
- $opionHtml
+ $optionHtml
)
);
diff --git a/classes/widgets/form/elements/widget_TermsAndConditionsElement.php b/classes/widgets/form/elements/widget_TermsAndConditionsElement.php
index 90de7d4..8ea41ce 100644
--- a/classes/widgets/form/elements/widget_TermsAndConditionsElement.php
+++ b/classes/widgets/form/elements/widget_TermsAndConditionsElement.php
@@ -29,11 +29,12 @@ public function render() {
}
function renderElement() {
- return(sf(" ",
+ return(sf(" ",
$this->termsText,
$this->renderErrors(),
$this->getName(),
$this->getValue() ? "checked='checked'" : "",
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
$this->renderLabel()
));
diff --git a/classes/widgets/form/elements/widget_TextAreaElement.php b/classes/widgets/form/elements/widget_TextAreaElement.php
index a1e394c..43c6685 100644
--- a/classes/widgets/form/elements/widget_TextAreaElement.php
+++ b/classes/widgets/form/elements/widget_TextAreaElement.php
@@ -4,6 +4,7 @@ class widget_TextAreaElement extends widget_AbstractElement {
private $disabled = false;
private $rows = 7;
+ private $placeholder = '';
public function __construct($args) {
if(array_key_exists("disabled", $args))
@@ -11,6 +12,9 @@ public function __construct($args) {
if(array_key_exists('rows', $args))
$this->rows = $args['rows'];
+
+ if(array_key_exists('placeholder', $args))
+ $this->placeholder = $args['placeholder'];
}
public function outputSpecific() {
@@ -18,8 +22,10 @@ public function outputSpecific() {
}
function renderElement() {
- return( sf(" ",
+ return( sf(" ",
$this->getName(),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'',
$this->getName(),
$this->disabled?" disabled='true'":"",
!is_null($this->rows)?sf(' rows="%s"',$this->rows):'',
diff --git a/classes/widgets/form/elements/widget_TextConfirmElement.php b/classes/widgets/form/elements/widget_TextConfirmElement.php
index 17fa42a..2da88e5 100644
--- a/classes/widgets/form/elements/widget_TextConfirmElement.php
+++ b/classes/widgets/form/elements/widget_TextConfirmElement.php
@@ -5,10 +5,15 @@ class widget_TextConfirmElement extends widget_AbstractElement {
protected $htmlType = 'text';
protected $confirmText = "Please confirm";
protected $cssClassName = 'inputTextConfirm';
+ protected $placeholder = '';
public function __construct($args) {
if(array_key_exists("confirmText", $args))
$this->confirmText = $args['confirmText'];
+
+ if (array_key_exists('placeholder', $args) && strlen($args['placeholder']))
+ $this->placeholder = $args['placeholder'];
+
}
function renderElement() {
@@ -18,18 +23,19 @@ function renderElement() {
if(!array_key_exists(0,$value)) $value[0] = "";
if(!array_key_exists(1,$value)) $value[1] = "";
- $out = sf(" ",
- $this->htmlType, $this->getName(), parent::makeInputSafe($value[0]), $this->getName(),
- $this->cssClassName, $this->cssClassName
+ $out = sf(" ",
+ $this->htmlType, $this->getName(), parent::makeInputSafe($value[0]), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(), $this->cssClassName, $this->cssClassName,
+ strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):''
);
- $out .= sf(" ",
+ $out .= sf(" ",
$this->getName(), $this->confirmText,
($this->getRequired() && !$this->getValidates()) ? $this->goAsterisks() : "");
- $out .= sf(" ",
- $this->htmlType, $this->getName(), parent::makeInputSafe($value[1]), $this->getName(),
- $this->cssClassName, $this->cssClassName
+ $out .= sf(" ",
+ $this->htmlType, $this->getName(), parent::makeInputSafe($value[1]), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(), $this->cssClassName, $this->cssClassName
);
return $out;
diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php
index 81c6cae..209cae9 100644
--- a/classes/widgets/form/elements/widget_TextElement.php
+++ b/classes/widgets/form/elements/widget_TextElement.php
@@ -5,23 +5,69 @@ class widget_TextElement extends widget_AbstractElement {
protected $htmlType = 'text';
protected $cssClassName = 'inputText';
protected $placeholder = '';
+ protected $onChange = null;
+ protected $onKeydown = null;
+ protected $onFocus = null;
+ protected $onBlur = null;
+ protected $disabled = false;
+ protected $attributes = array();
protected $rows;
public function __construct($args) {
if (array_key_exists('placeholder', $args) && strlen($args['placeholder']))
$this->placeholder = $args['placeholder'];
+
+ if (array_key_exists('type', $args) && strlen($args['type']))
+ $this->htmlType = $args['type'];
+
+ if (array_key_exists('onChange', $args) && strlen($args['onChange']))
+ $this->onChange = $args['onChange'];
+
+ if (array_key_exists('onKeydown', $args) && strlen($args['onKeydown']))
+ $this->onKeydown = $args['onKeydown'];
+
+ if (array_key_exists('onFocus', $args) && strlen($args['onFocus']))
+ $this->onFocus = $args['onFocus'];
+
+ if (array_key_exists('onBlur', $args) && strlen($args['onBlur']))
+ $this->onBlur = $args['onBlur'];
+
+ if (array_key_exists('disabled', $args) && is_bool($args['disabled']))
+ $this->disabled = $args['disabled'];
+
+
+ if (array_key_exists('attributes', $args) && is_array($args['attributes']))
+ $this->attributes = $args['attributes'];
+
+
+
}
function renderElement() {
- return(sf(' ',
- $this->htmlType,
- $this->getName(),
- parent::makeInputSafe($this->getValue()),
- $this->getName(),
- $this->cssClassName,
- strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):''
- ));
+
+ $attributesHtml = '';
+ if (count($this->attributes))
+ foreach ($this->attributes as $key => $val)
+ $attributesHtml.= sf(' %s="%s" ', $key, $val);
+
+
+ return(sf(' ',
+ $this->htmlType,
+ $this->getName(),
+ parent::makeInputSafe($this->getValue()),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
+ $this->getName(),
+ $this->cssClassName,
+ $attributesHtml,
+ strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'',
+ !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'',
+ !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'',
+ !is_null($this->onFocus) && strlen($this->onFocus)?sf(' onFocus="%s"', $this->onFocus):'',
+ !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'',
+ $this->disabled?' disabled':''
+ ));
}
+
}
?>
\ No newline at end of file
diff --git a/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php b/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php
index 9a5f2d7..3df56f8 100644
--- a/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php
+++ b/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php
@@ -17,10 +17,11 @@ function renderElement() {
if( $this->beforeText != '' )
$ret .= sf(' %s ', $this->beforeText);
- $ret .= sf(" ",
+ $ret .= sf(" ",
$this->htmlType,
$this->getName(),
parent::makeInputSafe($this->getValue()),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$this->getName(),
$this->inputStyle
);
diff --git a/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php b/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php
index bc4974b..5e78fa6 100644
--- a/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php
+++ b/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php
@@ -24,15 +24,17 @@ function renderElement() {
foreach($this->options as $value => $option) {
if(strval($elementValue) == strval($value)) {
$usedSuggested = true;
- $html .= sfl(' %s ',
+ $html .= sfl(' %s ',
$this->getName(),
parent::makeInputSafe($value),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$option
);
} else
- $html .= sfl(' %s ',
+ $html .= sfl(' %s ',
$this->getName(),
parent::makeInputSafe($value),
+ ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '',
$option
);
}
@@ -40,7 +42,7 @@ function renderElement() {
return sf(" ",
$html,
- sfl(' ', $this->getName(), $usedSuggested ? "" :'checked="checked"'),
+ sfl(' ', $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $usedSuggested ? "" :'checked="checked"'),
$this->beforeText,
$this->getName()."_custom",
$usedSuggested ? "" : parent::makeInputSafe($this->getValue()),
diff --git a/classes/widgets/form/elements/widget_UkAddressElement.php b/classes/widgets/form/elements/widget_UkAddressElement.php
index ec53b33..686408d 100644
--- a/classes/widgets/form/elements/widget_UkAddressElement.php
+++ b/classes/widgets/form/elements/widget_UkAddressElement.php
@@ -23,24 +23,24 @@ function renderElement() {
* The rendering of this element is SO messy due to IE compatibility - needs rewriting
*
*/
- $out = sf(" ",
- $this->getName(), parent::makeInputSafe($address['address1']), $this->getName()
+ $out = sf(" ",
+ $this->getName(), parent::makeInputSafe($address['address1']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName()
);
- $out .= sf(" ",
- $this->getName(), parent::makeInputSafe($address['address2']), $this->getName()
+ $out .= sf(" ",
+ $this->getName(), parent::makeInputSafe($address['address2']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName()
);
$out .= sf("
",
$this->getName(), "Post Code"
);
- $out .= sfl(" ",
+ $this->getName(), parent::makeInputSafe($address['town']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName()
);
return $out;
diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php
index 9e0a896..d115bcf 100644
--- a/classes/widgets/form/widget_Form.php
+++ b/classes/widgets/form/widget_Form.php
@@ -17,6 +17,8 @@ class widget_Form {
private $submitted = false;
private $ancorJump = false; // will the form jump to the ancor?
private $actionPath = '';
+ private $cssClasses = array();
+ private $onSubmit = null;
public function __construct($formName = null, $autoLoadFormData = true) {
if(!is_null($formName)) $this->setName($formName);
@@ -26,9 +28,12 @@ public function __construct($formName = null, $autoLoadFormData = true) {
public function __get($name) {
$matches = null;
if(preg_match('/^value_(.+)$/', $name, $matches)) {
- return call_user_func(array($this, 'value'), $matches [1]);
- }
- throw new Exception('Undefined method:' . $name);
+ return call_user_func(array($this, 'value'), $matches [1]);
+ } elseif(preg_match('/^valueOrNull_(.+)$/', $name, $matches)) {
+ return call_user_func(array($this, 'valueOrNull'), $matches [1]);
+ } elseif (preg_match('/^element_(.+)$/', $name, $matches)) {
+ return call_user_func(array($this, 'element'), $matches [1]);
+ } else throw new Exception('Undefined method:' . $name);
}
public function setName($in) {
@@ -49,6 +54,10 @@ public function getTitle() {
return $this->title;
}
+ public function addCssClass ($className) {
+ $this->cssClasses[] = $className;
+ }
+
public function setFormDataFromMethod() {
switch($this->method) {
@@ -96,7 +105,19 @@ public function add($elementClass, $args) {
// css style
if(array_key_exists('style',$args))
$element->setStyle($args['style']);
-
+
+ // css style
+ if(array_key_exists('cssStyle',$args))
+ $element->setCssStyle($args['cssStyle']);
+
+ // tabindexes
+ if(array_key_exists('tabindex',$args))
+ $element->setTabindex($args['tabindex']);
+
+ // css style
+ if(array_key_exists('cssClass',$args))
+ $element->setCssClass($args['cssClass']);
+
if(array_key_exists('force_default', $args))
$element->setForceDefault($args['force_default']);
@@ -106,6 +127,7 @@ public function add($elementClass, $args) {
try {
$element->setValue($this->userInput, $this->userFiles);
} catch(Exception $e) {
+ Atsumi::error__listen($e);
$element->setError($e);
}
$element->validate();
@@ -115,13 +137,46 @@ public function add($elementClass, $args) {
$this->elementMap[$element->getName()] = &$element;
}
+ public function remove($elementName) {
+ if(!isset($this->elementMap[$elementName])) {
+ throw new Exception('Cannot remove non-existant element');
+ }
+ for($i = 0; $i < count($this->elements); $i++) {
+ $done = false;
+ if($this->elementMap[$elementName] == $this->elements[$i]) {
+ unset($this->elementMap[$elementName]);
+ unset($this->elements[$i]);
+ $done = true;
+ }
+ if($done) {
+ $this->elements = array_values($this->elements);
+ break;
+ }
+ }
+ }
+
public function value($elementName) {
if(!array_key_exists($elementName, $this->elementMap))
throw new Exception("Element not found: ".$elementName);
-
+
return $this->elementMap[$elementName]->getValue();
}
+ public function valueOrNull($elementName) {
+ $value = $this->value($elementName);
+
+ return trim($value)==''?null:$value;
+ }
+
+ /* Returns an element */
+ public function element ($elementName) {
+
+ if(!array_key_exists($elementName, $this->elementMap))
+ throw new Exception("Element not found: ".$elementName);
+
+ return $this->elementMap[$elementName];
+ }
+
public function getSubmitted() {
return $this->submitted;
}
@@ -138,6 +193,10 @@ public function setAncorJump($jump) {
$this->ancorJump = $jump;
}
+ public function setOnSubmit($in) {
+ $this->onSubmit = $in;
+ }
+
public function setSubmit($in) {
if(!is_string($in))
throw new Exception("Submit text should be of type String");
@@ -169,7 +228,7 @@ public function __toString() {
return strval($this->render());
}
- public function render() {
+ public function render($options = array()) {
// totally forgotten what this force defaults is, do we need it?...
// forceDefaults is if you want to force the form to show its default values on reload
@@ -180,30 +239,38 @@ public function render() {
$row->validate();
}
- $html = $this->getFormTop();
+ $html = $this->getFormTop(isset($options['top'])?$options['top']:array());
foreach($this->elements as $element) {
$html .= sfl("%s", $element->render());
}
- $html .= $this->getFormBottom();
+ $html .= $this->getFormBottom(isset($options['bottom'])?$options['bottom']:array());
return $html;
}
- public function getElement($elementName) {
- return $this->elementMap[$elementName]->render();
+ public function hasElement($elementName) {
+ return array_key_exists($elementName, $this->elementMap);
}
- public function getFormTop() {
+ public function getElement($elementName, $options = array()) {
+ return $this->elementMap[$elementName]->render($options);
+ }
- $html = sf(' ');
return $html;
diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php
index b4a5ce8..e523a23 100644
--- a/classes/widgets/pagination/widget_Paginate.php
+++ b/classes/widgets/pagination/widget_Paginate.php
@@ -26,13 +26,14 @@ class widget_Paginate {
/* format presets/templates */
const TEMPLATE_CLASSIC = 1;
const TEMPLATE_ARROWS = 2;
+ const TEMPLATE_SIMPLE = 3;
/* member variables used for pagination calculations */
private $recordCount;
private $pageCount;
private $resultsPerPage = 10;
private $currentPage = 0;
- private $navLength = 7;
+ private $navLength = 4;
private $url;
/* formatting options */
@@ -67,7 +68,7 @@ public function __toString() {
return $this->render();
}
- private function generateUrl($page) {
+ public function generateUrl($page) {
return str_replace("[PAGE]", $page, $this->url);
}
@@ -75,6 +76,14 @@ private function generateUrl($page) {
public function getPageCount() {
return $this->pageCount;
}
+ public function getPage() {
+ return $this->currentPage;
+ }
+
+ /* returns total page count */
+ public function getTotalResults() {
+ return $this->recordCount;
+ }
/* returns current page */
public function getCurrentPage() {
@@ -96,24 +105,35 @@ public function getLimit() {
return $this->resultsPerPage;
}
+
+
+ public function getResultsPerPage() {
+ return $this->resultsPerPage;
+ }
+
- /* set the format template (default is classic) */
- public function setFormatTemplate ($template = 1) {
+ /* set the format template (default is simple) */
+ public function setFormatTemplate ($template = 3) {
switch ($template) {
default:
throw new Exception ('Unknown pagination template');
case self::TEMPLATE_CLASSIC:
- $this->format = '';
+ $this->format = '';
$this->formatNext = array('', '');
$this->formatPrevious = array('', '');
break;
- case self::TEMPLATE_ARROWS:
- $this->format = '';
- $this->formatNext = array(' »', ' »');
- $this->formatPrevious = array(' «', ' «');
+ case self::TEMPLATE_ARROWS:
+ $this->format = '';
+ $this->formatNext = array(' »', ' »');
+ $this->formatPrevious = array(' «', ' «');
+ break;
+
+ case self::TEMPLATE_SIMPLE:
+ $this->format = '';
+ $this->formatNext = array(' Next', '');
break;
}
@@ -187,11 +207,11 @@ private function setPageCountByResultsPerPage($in) {
/* returns a html page link for inclusion in the pagination output */
public function renderPageLink ($page) {
- return sf(" %s",
+ return sf(" %s",
($this->currentPage == $page) ? " currentPage" : '',
- ($this->currentPage == $page) ?
- number_format($page) :
- sprintf(" %s", $this->generateUrl($page),number_format($page))
+ $this->generateUrl($page),
+ ($this->currentPage == $page) ? " on":'',
+ number_format($page)
);
}
@@ -207,9 +227,15 @@ public function render () {
$start = $this->renderPageLink(1);
$end = $this->getPageCount() < 2?'':$this->renderPageLink($this->getPageCount());
+
// params: Pages links
$pageLength = $this->navLength;
- if ($pageLength&1) { } else $pageLength++;
+ if ($pageLength&1 ||
+ (array_key_exists('[START]', $options) && !array_key_exists('[END]', $options)) ||
+ (!array_key_exists('[START]', $options) && array_key_exists('[END]', $options))
+ ) { }
+
+ else $pageLength++;
if (array_key_exists('[START]', $options)) $pageLength--;
if (array_key_exists('[END]', $options)) $pageLength--;
@@ -230,23 +256,24 @@ public function render () {
if ($pageLinkStart < 1) $pageLinkStart = 1;
if (array_key_exists('[START]', $options) && $pageLinkStart == 1) $pageLinkStart = 2;
+
$pages = '';
if ($pageLinkStart <= $pageLinkEnd)
for ($i = $pageLinkStart; $i <= $pageLinkEnd; $i++)
$pages .= $this->renderPageLink($i);
// params: start ellipses
- if (array_key_exists('[START_ELLIPSES]', $options) && array_key_exists('[START]', $options) &&
+ if (array_key_exists('[START_ELLIPSES]', $options) &&
$pageLinkStart > 2) $startEllipses = ' ...';
else $startEllipses = '';
// params: end ellipses
- if (array_key_exists('[END_ELLIPSES]', $options) && array_key_exists('[END]', $options) &&
+ if (array_key_exists('[END_ELLIPSES]', $options) &&
$pageLinkEnd < ($this->pageCount-1)) $endEllipses = ' ...';
else $endEllipses = '';
// params: next link
- if ($this->currentPage == $this->pageCount) $next = $this->formatNext[1];
+ if ($this->currentPage >= $this->pageCount) $next = $this->formatNext[1];
else $next = str_replace('[HREF]', $this->generateUrl($this->currentPage+1), $this->formatNext[0]);
// params: previous link
diff --git a/include/http.php b/include/http.php
index 8b5fb81..68d8c27 100644
--- a/include/http.php
+++ b/include/http.php
@@ -1,4 +1,4 @@
-
+ |