Update from AER.NEW

This commit is contained in:
Deon George 2014-12-10 11:52:26 +11:00
parent b65ee7b736
commit 6e433c574d
22 changed files with 38 additions and 2164 deletions

View File

@ -1,86 +0,0 @@
# Kohana-Cron
This module provides a way to schedule tasks (jobs) within your Kohana application.
## Installation
Step 1: Download the module into your modules subdirectory.
Step 2: Enable the module in your bootstrap file:
/**
* Enable modules. Modules are referenced by a relative or absolute path.
*/
Kohana::modules(array(
'cron' => MODPATH.'cron',
// 'auth' => MODPATH.'auth', // Basic authentication
// 'codebench' => MODPATH.'codebench', // Benchmarking tool
// 'database' => MODPATH.'database', // Database access
// 'image' => MODPATH.'image', // Image manipulation
// 'orm' => MODPATH.'orm', // Object Relationship Mapping
// 'pagination' => MODPATH.'pagination', // Paging of results
// 'userguide' => MODPATH.'userguide', // User guide and API documentation
));
Step 3: Make sure the settings in `config/cron.php` are correct for your environment.
If not, copy the file to `application/config/cron.php` and change the values accordingly.
## Usage
In its simplest form, a task is a [PHP callback][1] and times at which it should run.
To configure a task call `Cron::set($name, array($frequency, $callback))` where
`$frequency` is a string of date and time fields identical to those found in [crontab][2].
For example,
Cron::set('reindex_catalog', array('@daily', 'Catalog::regenerate_index'));
Cron::set('calendar_notifications', array('*/5 * * * *', 'Calendar::send_emails'));
Configured tasks are run with their appropriate frequency by calling `Cron::run()`. Call
this method in your bootstrap file, and you're done!
## Advanced Usage
A task can also be an instance of `Cron` that extends `next()` and/or `execute()` as
needed. Such a task is configured by calling `Cron::set($name, $instance)`.
If you have access to the system crontab, you can run Cron less (or more) than once
every request. You will need to modify the lines where the request is handled in your
bootstrap file to prevent extraneous output. The default is:
/**
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
echo Request::instance()
->execute()
->send_headers()
->response;
Change it to:
if ( ! defined('SUPPRESS_REQUEST'))
{
/**
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
echo Request::instance()
->execute()
->send_headers()
->response;
}
Then set up a system cron job to run your application's Cron once a minute:
* * * * * /usr/bin/php -f /path/to/kohana/modules/cron/run.php
The included `run.php` should work for most cases, but you are free to call `Cron::run()`
in any way you see fit.
[1]: http://php.net/manual/language.pseudo-types.php#language.types.callback
[2]: http://linux.die.net/man/5/crontab

View File

@ -1,10 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* @package Cron
*
* @author Chris Bandy
* @copyright (c) 2010 Chris Bandy
* @license http://www.opensource.org/licenses/isc-license.txt
*/
class Cron extends Kohana_Cron {}

View File

@ -1,621 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* @package Cron
*
* @author Chris Bandy
* @copyright (c) 2010 Chris Bandy
* @license http://www.opensource.org/licenses/isc-license.txt
*/
class Kohana_Cron
{
protected static $_jobs = array();
protected static $_times = array();
/**
* Registers a job to be run
*
* @param string Unique name
* @param array|Cron Job to run
*/
public static function set($name, $job)
{
if (is_array($job))
{
$job = new Cron(reset($job), next($job));
}
Cron::$_jobs[$name] = $job;
}
/**
* Retrieve the timestamps of when jobs should run
*/
protected static function _load()
{
Cron::$_times = Kohana::cache("Cron::run()");
}
/**
* Acquire the Cron mutex
*
* @return boolean
*/
protected static function _lock()
{
$config = Kohana::$config->load('cron');
$result = FALSE;
if (file_exists($config->lock) AND ($stat = @stat($config->lock)) AND time() - $config->window < $stat['mtime'])
{
// Lock exists and has not expired
return $result;
}
$fh = fopen($config->lock, 'a');
if (flock($fh, LOCK_EX))
{
fseek($fh, 0, SEEK_END);
if (ftell($fh) === (empty($stat) ? 0 : $stat['size']))
{
// Current size matches expected size
// Claim the file by changing the size
fwrite($fh, '.');
$result = TRUE;
}
// else, Another process acquired during flock()
}
fclose($fh);
return $result;
}
/**
* Store the timestamps of when jobs should run next
*/
protected static function _save()
{
Kohana::cache("Cron::run()", Cron::$_times, Kohana::$config->load('cron')->window * 2);
}
/**
* Release the Cron mutex
*/
protected static function _unlock()
{
return @unlink(Kohana::$config->load('cron')->lock);
}
/**
* @return boolean FALSE when another instance is running
*/
public static function run()
{
if (empty(Cron::$_jobs))
return TRUE;
if ( ! Cron::_lock())
return FALSE;
try
{
Cron::_load();
$now = time();
$threshold = $now - Kohana::$config->load('cron')->window;
foreach (Cron::$_jobs as $name => $job)
{
if (empty(Cron::$_times[$name]) OR Cron::$_times[$name] < $threshold)
{
// Expired
Cron::$_times[$name] = $job->next($now);
if ($job->next($threshold) < $now)
{
// Within the window
$job->execute();
}
}
elseif (Cron::$_times[$name] < $now)
{
// Within the window
Cron::$_times[$name] = $job->next($now);
$job->execute();
}
}
}
catch (Exception $e) {}
Cron::_save();
Cron::_unlock();
if (isset($e))
throw $e;
return TRUE;
}
protected $_callback;
protected $_period;
public function __construct($period, $callback)
{
$this->_period = $period;
$this->_callback = $callback;
}
/**
* Execute this job
*/
public function execute()
{
call_user_func($this->_callback);
}
/**
* Calculates the next timestamp in this period
*
* @param integer Timestamp from which to calculate
* @return integer Next timestamp in this period
*/
public function next($from)
{
// PHP >= 5.3.0
//if ($this->_period instanceof DatePeriod) { return; }
//if (is_string($this->_period) AND preg_match('/^P[\dDHMSTWY]+$/', $period)) { $this->_period = new DateInterval($this->_period); }
//if ($this->_period instanceof DateInterval) { return; }
return $this->_next_crontab($from);
}
/**
* Calculates the next timestamp of this crontab period
*
* @param integer Timestamp from which to calculate
* @return integer Next timestamp in this period
*/
protected function _next_crontab($from)
{
if (is_string($this->_period))
{
// Convert string to lists of valid values
if ($this->_period[0] === '@')
{
switch (substr($this->_period, 1))
{
case 'annually':
case 'yearly':
// '0 0 1 1 *'
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => array(1), 'months' => array(1), 'weekdays' => range(0,6));
break;
case 'daily':
case 'midnight':
// '0 0 * * *'
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => range(0,6));
break;
case 'hourly':
// '0 * * * *'
$this->_period = array('minutes' => array(0), 'hours' => range(0,23), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => range(0,6));
break;
case 'monthly':
// '0 0 1 * *'
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => array(1), 'months' => range(1,12), 'weekdays' => range(0,6));
break;
case 'weekly':
// '0 0 * * 0'
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => array(0));
break;
}
}
else
{
list($minutes, $hours, $monthdays, $months, $weekdays) = explode(' ', $this->_period);
$months = strtr(strtolower($months), array(
'jan' => 1,
'feb' => 2,
'mar' => 3,
'apr' => 4,
'may' => 5,
'jun' => 6,
'jul' => 7,
'aug' => 8,
'sep' => 9,
'oct' => 10,
'nov' => 11,
'dec' => 12,
));
$weekdays = strtr(strtolower($weekdays), array(
'sun' => 0,
'mon' => 1,
'tue' => 2,
'wed' => 3,
'thu' => 4,
'fri' => 5,
'sat' => 6,
));
$this->_period = array(
'minutes' => $this->_parse_crontab_field($minutes, 0, 59),
'hours' => $this->_parse_crontab_field($hours, 0, 23),
'monthdays' => $this->_parse_crontab_field($monthdays, 1, 31),
'months' => $this->_parse_crontab_field($months, 1, 12),
'weekdays' => $this->_parse_crontab_field($weekdays, 0, 7)
);
// Ensure Sunday is zero
if (end($this->_period['weekdays']) === 7)
{
array_pop($this->_period['weekdays']);
if (reset($this->_period['weekdays']) !== 0)
{
array_unshift($this->_period['weekdays'], 0);
}
}
}
}
$from = getdate($from);
if ( ! in_array($from['mon'], $this->_period['months']))
return $this->_next_crontab_month($from);
if (count($this->_period['weekdays']) === 7)
{
// Day of Week is unrestricted, defer to Day of Month
if ( ! in_array($from['mday'], $this->_period['monthdays']))
return $this->_next_crontab_monthday($from);
}
elseif (count($this->_period['monthdays']) === 31)
{
// Day of Month is unrestricted, use Day of Week
if ( ! in_array($from['wday'], $this->_period['weekdays']))
return $this->_next_crontab_weekday($from);
}
else
{
// Both Day of Week and Day of Month are restricted
if ( ! in_array($from['mday'], $this->_period['monthdays']) AND ! in_array($from['wday'], $this->_period['weekdays']))
return $this->_next_crontab_day($from);
}
if ( ! in_array($from['hours'], $this->_period['hours']))
return $this->_next_crontab_hour($from);
return $this->_next_crontab_minute($from);
}
/**
* Calculates the first timestamp in the next day of this period when both
* Day of Week and Day of Month are restricted
*
* @uses _next_crontab_month()
*
* @param array Date array from getdate()
* @return integer Timestamp of next restricted Day
*/
protected function _next_crontab_day(array $from)
{
// Calculate effective Day of Month for next Day of Week
if ($from['wday'] >= end($this->_period['weekdays']))
{
$next = reset($this->_period['weekdays']) + 7;
}
else
{
foreach ($this->_period['weekdays'] as $next)
{
if ($from['wday'] < $next)
break;
}
}
$monthday = $from['mday'] + $next - $from['wday'];
if ($monthday <= (int) date('t', mktime(0, 0, 0, $from['mon'], 1, $from['year'])))
{
// Next Day of Week is in this Month
if ($from['mday'] >= end($this->_period['monthdays']))
{
// No next Day of Month, use next Day of Week
$from['mday'] = $monthday;
}
else
{
// Calculate next Day of Month
foreach ($this->_period['monthdays'] as $next)
{
if ($from['mday'] < $next)
break;
}
// Use earliest day
$from['mday'] = min($monthday, $next);
}
}
else
{
if ($from['mday'] >= end($this->_period['monthdays']))
{
// No next Day of Month, use next Month
return $this->_next_crontab_month($from);
}
// Calculate next Day of Month
foreach ($this->_period['monthdays'] as $next)
{
if ($from['mday'] < $next)
break;
}
// Use next Day of Month
$from['mday'] = $next;
}
// Use first Hour and first Minute
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
}
/**
* Calculates the first timestamp in the next hour of this period
*
* @uses _next_crontab_day()
* @uses _next_crontab_monthday()
* @uses _next_crontab_weekday()
*
* @param array Date array from getdate()
* @return integer Timestamp of next Hour
*/
protected function _next_crontab_hour(array $from)
{
if ($from['hours'] >= end($this->_period['hours']))
{
// No next Hour
if (count($this->_period['weekdays']) === 7)
{
// Day of Week is unrestricted, defer to Day of Month
return $this->_next_crontab_monthday($from);
}
if (count($this->_period['monthdays']) === 31)
{
// Day of Month is unrestricted, use Day of Week
return $this->_next_crontab_weekday($from);
}
// Both Day of Week and Day of Month are restricted
return $this->_next_crontab_day($from);
}
// Calculate next Hour
foreach ($this->_period['hours'] as $next)
{
if ($from['hours'] < $next)
break;
}
// Use next Hour and first Minute
return mktime($next, reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
}
/**
* Calculates the timestamp of the next minute in this period
*
* @uses _next_crontab_hour()
*
* @param array Date array from getdate()
* @return integer Timestamp of next Minute
*/
protected function _next_crontab_minute(array $from)
{
if ($from['minutes'] >= end($this->_period['minutes']))
{
// No next Minute, use next Hour
return $this->_next_crontab_hour($from);
}
// Calculate next Minute
foreach ($this->_period['minutes'] as $next)
{
if ($from['minutes'] < $next)
break;
}
// Use next Minute
return mktime($from['hours'], $next, 0, $from['mon'], $from['mday'], $from['year']);
}
/**
* Calculates the first timestamp in the next month of this period
*
* @param array Date array from getdate()
* @return integer Timestamp of next Month
*/
protected function _next_crontab_month(array $from)
{
if ($from['mon'] >= end($this->_period['months']))
{
// No next Month, increment Year and use first Month
++$from['year'];
$from['mon'] = reset($this->_period['months']);
}
else
{
// Calculate next Month
foreach ($this->_period['months'] as $next)
{
if ($from['mon'] < $next)
break;
}
// Use next Month
$from['mon'] = $next;
}
if (count($this->_period['weekdays']) === 7)
{
// Day of Week is unrestricted, use first Day of Month
$from['mday'] = reset($this->_period['monthdays']);
}
else
{
// Calculate Day of Month for the first Day of Week
$indices = array_flip($this->_period['weekdays']);
$monthday = 1;
$weekday = (int) date('w', mktime(0, 0, 0, $from['mon'], 1, $from['year']));
while ( ! isset($indices[$weekday % 7]) AND $monthday < 7)
{
++$monthday;
++$weekday;
}
if (count($this->_period['monthdays']) === 31)
{
// Day of Month is unrestricted, use first Day of Week
$from['mday'] = $monthday;
}
else
{
// Both Day of Month and Day of Week are restricted, use earliest one
$from['mday'] = min($monthday, reset($this->_period['monthdays']));
}
}
// Use first Hour and first Minute
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
}
/**
* Calculates the first timestamp in the next day of this period when only
* Day of Month is restricted
*
* @uses _next_crontab_month()
*
* @param array Date array from getdate()
* @return integer Timestamp of next Day of Month
*/
protected function _next_crontab_monthday(array $from)
{
if ($from['mday'] >= end($this->_period['monthdays']))
{
// No next Day of Month, use next Month
return $this->_next_crontab_month($from);
}
// Calculate next Day of Month
foreach ($this->_period['monthdays'] as $next)
{
if ($from['mday'] < $next)
break;
}
// Use next Day of Month, first Hour, and first Minute
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $next, $from['year']);
}
/**
* Calculates the first timestamp in the next day of this period when only
* Day of Week is restricted
*
* @uses _next_crontab_month()
*
* @param array Date array from getdate()
* @return integer Timestamp of next Day of Week
*/
protected function _next_crontab_weekday(array $from)
{
// Calculate effective Day of Month for next Day of Week
if ($from['wday'] >= end($this->_period['weekdays']))
{
$next = reset($this->_period['weekdays']) + 7;
}
else
{
foreach ($this->_period['weekdays'] as $next)
{
if ($from['wday'] < $next)
break;
}
}
$monthday = $from['mday'] + $next - $from['wday'];
if ($monthday > (int) date('t', mktime(0, 0, 0, $from['mon'], 1, $from['year'])))
{
// Next Day of Week is not in this Month, use next Month
return $this->_next_crontab_month($from);
}
// Use next Day of Week, first Hour, and first Minute
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $monthday, $from['year']);
}
/**
* Returns a sorted array of all the values indicated in a Crontab field
* @link http://linux.die.net/man/5/crontab
*
* @param string Crontab field
* @param integer Minimum value for this field
* @param integer Maximum value for this field
* @return array
*/
protected function _parse_crontab_field($value, $min, $max)
{
$result = array();
foreach (explode(',', $value) as $value)
{
if ($slash = strrpos($value, '/'))
{
$step = (int) substr($value, $slash + 1);
$value = substr($value, 0, $slash);
}
if ($value === '*')
{
$result = array_merge($result, range($min, $max, $slash ? $step : 1));
}
elseif ($dash = strpos($value, '-'))
{
$result = array_merge($result, range(max($min, (int) substr($value, 0, $dash)), min($max, (int) substr($value, $dash + 1)), $slash ? $step : 1));
}
else
{
$value = (int) $value;
if ($min <= $value AND $value <= $max)
{
$result[] = $value;
}
}
}
sort($result);
return array_unique($result);
}
}

View File

@ -1,28 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* @package Cron
*
* @author Chris Bandy
* @copyright (c) 2010 Chris Bandy
* @license http://www.opensource.org/licenses/isc-license.txt
*/
return array
(
// Path to a writable directory and lock file
'lock' => Kohana::$cache_dir.DIRECTORY_SEPARATOR.'cron.lck',
/**
* Cron does not run EXACTLY when tasks are scheduled.
* A task can be executed up to this many seconds AFTER its scheduled time.
*
* For example, Cron is run at 10:48 and a task was scheduled to execute at
* 10:45, 180 seconds ago. If window is greater than 180, the task will be
* executed.
*
* This value should always be larger than the time it takes to run all
* your tasks.
*/
'window' => 300,
);

View File

@ -1,22 +0,0 @@
<?php
/**
* @package Cron
*
* @author Chris Bandy
* @copyright (c) 2010 Chris Bandy
* @license http://www.opensource.org/licenses/isc-license.txt
*/
// Path to Kohana's index.php
$system = dirname(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'index.php';
if (file_exists($system))
{
defined('SUPPRESS_REQUEST') or define('SUPPRESS_REQUEST', TRUE);
include $system;
// If Cron has been run in APPPATH/bootstrap.php, this second call is harmless
Cron::run();
}

View File

@ -1,85 +0,0 @@
<?php
/**
* @package Cron
* @group kohana
* @group kohana.cron
*
* @author Chris Bandy
* @copyright (c) 2010 Chris Bandy
* @license http://www.opensource.org/licenses/isc-license.txt
*/
class Kohana_Cron_Test extends PHPUnit_Framework_TestCase
{
/**
* @test
* @dataProvider provider_next
*
* @param string Period
* @param integer Timestamp from which to calculate
* @param integer Next timestamp in period
*/
public function test_next($period, $from, $expected_result)
{
$cron = new Cron($period, NULL);
$result = $cron->next($from);
$this->assertSame($expected_result, $result);
}
public function provider_next()
{
return array
(
array('@annually', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 1, 1, 2010)),
array('@monthly', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 12, 1, 2009)),
array('@weekly', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 11, 22, 2009)),
array('@daily', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 11, 20, 2009)),
array('@hourly', mktime(8, 45, 0, 11, 19, 2009), mktime(9, 0, 0, 11, 19, 2009)),
array('* * * * *', mktime(8, 45, 0, 11, 19, 2009), mktime(8, 46, 0, 11, 19, 2009)),
array(
'* * * * 0', // Sundays
mktime(0, 0, 0, 11, 30, 2009), // Monday, Nov 30, 2009
mktime(0, 0, 0, 12, 6, 2009) // Sunday, Dec 6, 2009
),
array(
'* * 15 * 6', // 15th and Saturdays
mktime(0, 0, 0, 11, 29, 2009), // Sunday, Nov 29, 2009
mktime(0, 0, 0, 12, 5, 2009) // Saturday, Dec 5, 2009
),
array(
'* * * * 1,5', // Mondays and Fridays
mktime(0, 0, 0, 11, 24, 2009), // Tuesday, Nov 24, 2009
mktime(0, 0, 0, 11, 27, 2009) // Friday, Nov 27, 2009
),
array(
'* * 15 * 6-7', // 15th, Saturdays, and Sundays
mktime(0, 0, 0, 11, 23, 2009), // Monday, Nov 23, 2009
mktime(0, 0, 0, 11, 28, 2009) // Saturday, Nov 28, 2009
),
array(
'* * 15,30 * 2', // 15th, 30th, and Tuesdays
mktime(0, 0, 0, 11, 29, 2009), // Sunday, Nov 29, 2009
mktime(0, 0, 0, 11, 30, 2009) // Monday, Nov 30, 2009
),
array(
'0 0 * * 4', // Midnight on Thursdays
mktime(1, 0, 0, 11, 19, 2009), // 01:00 Thursday, Nov 19, 2009
mktime(0, 0, 0, 11, 26, 2009) // 00:00 Thursday, Nov 26, 2009
),
array(
'0 0 */2 * 4', // Midnight on odd days and Thursdays
mktime(1, 0, 0, 11, 19, 2009), // 01:00 Thursday, Nov 19, 2009
mktime(0, 0, 0, 11, 21, 2009) // 00:00 Saturday, Nov 21, 2009
),
);
}
}

View File

@ -92,7 +92,7 @@ class Kohana_Minion_CLI {
{ {
foreach ($values as $opt => $value) foreach ($values as $opt => $value)
{ {
if ( ! in_array($opt, $options, TRUE)) if ( ! in_array($opt, $options))
{ {
// Set the given value // Set the given value
unset($values[$opt]); unset($values[$opt]);

View File

@ -183,15 +183,6 @@ class Kohana_ORM extends Model implements serializable {
*/ */
protected $_table_names_plural = TRUE; protected $_table_names_plural = TRUE;
// Suppress ORMs inclusion of <table_name>.*
protected $_disable_wild_select = FALSE;
// Suppress ORMs inclusion of <table_name>. to column joins
protected $_disable_join_table_name = FALSE;
// Suppress ORMs use of limit
protected $_disable_limit = FALSE;
/** /**
* Model configuration, reload on wakeup? * Model configuration, reload on wakeup?
* @var bool * @var bool
@ -277,7 +268,7 @@ class Kohana_ORM extends Model implements serializable {
else else
{ {
// Passing the primary key // Passing the primary key
$this->where(($this->_disable_join_table_name ? '' : $this->_object_name.'.').$this->_primary_key, '=', $id)->find(); $this->where($this->_object_name.'.'.$this->_primary_key, '=', $id)->find();
} }
} }
elseif ( ! empty($this->_cast_data)) elseif ( ! empty($this->_cast_data))
@ -360,7 +351,6 @@ class Kohana_ORM extends Model implements serializable {
} }
$defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix;
$defaults['far_key'] = Inflector::singular($alias).$this->_foreign_key_suffix;
$init['_has_one'][$alias] = array_merge($defaults, $details); $init['_has_one'][$alias] = array_merge($defaults, $details);
} }
@ -369,7 +359,7 @@ class Kohana_ORM extends Model implements serializable {
{ {
if ( ! isset($details['model'])) if ( ! isset($details['model']))
{ {
$defaults['model'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', ($this->_model_names_plural ? Inflector::singular($alias) : $alias)))); $defaults['model'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', Inflector::singular($alias))));
} }
$defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix;
@ -636,7 +626,7 @@ class Kohana_ORM extends Model implements serializable {
$model = $this->_related($column); $model = $this->_related($column);
// Use this model's column and foreign model's primary key // Use this model's column and foreign model's primary key
$col = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$model->_primary_key; $col = $model->_object_name.'.'.$model->_primary_key;
$val = $this->_object[$this->_belongs_to[$column]['foreign_key']]; $val = $this->_object[$this->_belongs_to[$column]['foreign_key']];
// Make sure we don't run WHERE "AUTO_INCREMENT column" = NULL queries. This would // Make sure we don't run WHERE "AUTO_INCREMENT column" = NULL queries. This would
@ -653,23 +643,9 @@ class Kohana_ORM extends Model implements serializable {
{ {
$model = $this->_related($column); $model = $this->_related($column);
if (! is_array($this->_has_one[$column]['foreign_key'])) // Use this model's primary key value and foreign model's column
{ $col = $model->_object_name.'.'.$this->_has_one[$column]['foreign_key'];
// Use this model's primary key value and foreign model's column $val = $this->pk();
$col = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$this->_has_one[$column]['foreign_key'];
$val = $this->_object[$this->_has_one[$column]['far_key']];
}
else
{
foreach ($this->_has_one[$column]['foreign_key'] as $fk)
{
// Simple has_many relationship, search where target model's foreign key is this model's primary key
$col = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$fk;
$val = $this->_object[$fk];
$model = $model->where($col, '=', $val);
}
}
$model->where($col, '=', $val)->find(); $model->where($col, '=', $val)->find();
@ -679,52 +655,29 @@ class Kohana_ORM extends Model implements serializable {
{ {
$model = ORM::factory($this->_has_many[$column]['model']); $model = ORM::factory($this->_has_many[$column]['model']);
if (! is_array($this->_has_many[$column]['foreign_key'])) if (isset($this->_has_many[$column]['through']))
{ {
if (isset($this->_has_many[$column]['through'])) // Grab has_many "through" relationship table
{ $through = $this->_has_many[$column]['through'];
// Grab has_many "through" relationship table
$through = $this->_has_many[$column]['through'];
// Join on through model's target foreign key (far_key) and target model's primary key // Join on through model's target foreign key (far_key) and target model's primary key
$join_col1 = ($this->_disable_join_table_name ? '' : $through.'.').$this->_has_many[$column]['far_key']; $join_col1 = $through.'.'.$this->_has_many[$column]['far_key'];
$join_col2 = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$model->_primary_key; $join_col2 = $model->_object_name.'.'.$model->_primary_key;
$model->join($through)->on($join_col1, '=', $join_col2); $model->join($through)->on($join_col1, '=', $join_col2);
// Through table's source foreign key (foreign_key) should be this model's primary key // Through table's source foreign key (foreign_key) should be this model's primary key
$col = ($this->_disable_join_table_name ? '' : $through.'.').$this->_has_many[$column]['foreign_key']; $col = $through.'.'.$this->_has_many[$column]['foreign_key'];
$val = $this->pk(); $val = $this->pk();
}
else
{
// Simple has_many relationship, search where target model's foreign key is this model's primary key
$col = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$this->_has_many[$column]['foreign_key'];
$val = $this->_object[$this->_has_many[$column]['far_key']];
}
return $model->where($col, '=', $val);
} }
else else
{ {
foreach ($this->_has_many[$column]['foreign_key'] as $mk => $fk) // Simple has_many relationship, search where target model's foreign key is this model's primary key
{ $col = $model->_object_name.'.'.$this->_has_many[$column]['foreign_key'];
if (isset($this->_has_many[$column]['through'])) $val = $this->pk();
{
throw new Kohana_Exception('This code hasnt been written yet!');
}
else
{
// Simple has_many relationship, search where target model's foreign key is this model's primary key
$col = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$fk;
$val = $this->_object[$mk];
}
$model = $model->where($col, '=', $val);
}
return $model;
} }
return $model->where($col, '=', $val);
} }
else else
{ {
@ -1078,15 +1031,14 @@ class Kohana_ORM extends Model implements serializable {
{ {
$this->_db_builder->from(array($this->_table_name, $this->_object_name)); $this->_db_builder->from(array($this->_table_name, $this->_object_name));
if ($multiple === FALSE AND ! $this->_disable_limit) if ($multiple === FALSE)
{ {
// Only fetch 1 record // Only fetch 1 record
$this->_db_builder->limit(1); $this->_db_builder->limit(1);
} }
// Select all columns by default // Select all columns by default
if (! $this->_disable_wild_select) $this->_db_builder->select_array($this->_build_select());
$this->_db_builder->select_array($this->_build_select());
if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting)) if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting))
{ {
@ -1095,7 +1047,7 @@ class Kohana_ORM extends Model implements serializable {
if (strpos($column, '.') === FALSE) if (strpos($column, '.') === FALSE)
{ {
// Sorting column for use in JOINs // Sorting column for use in JOINs
$column = ($this->_disable_join_table_name ? '' : $this->_object_name.'.').$column; $column = $this->_object_name.'.'.$column;
} }
$this->_db_builder->order_by($column, $direction); $this->_db_builder->order_by($column, $direction);
@ -1213,10 +1165,9 @@ class Kohana_ORM extends Model implements serializable {
* @param string $value The value to filter * @param string $value The value to filter
* @return string * @return string
*/ */
protected function run_filter($field, $value, $filters=NULL) protected function run_filter($field, $value)
{ {
if (is_null($filters)) $filters = $this->filters();
$filters = $this->filters();
// Get the filters for this column // Get the filters for this column
$wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE]; $wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE];

View File

@ -15,15 +15,13 @@ $application = 'application';
*/ */
$modules = 'modules'; $modules = 'modules';
$sysmodules = 'includes/kohana/modules';
/** /**
* The directory in which the Kohana resources are located. The system * The directory in which the Kohana resources are located. The system
* directory must contain the classes/kohana.php file. * directory must contain the classes/kohana.php file.
* *
* @link http://kohanaframework.org/guide/about.install#system * @link http://kohanaframework.org/guide/about.install#system
*/ */
$system = 'includes/kohana/system'; $system = 'system';
/** /**
* The default extension of resource files. If you change this, all resources * The default extension of resource files. If you change this, all resources
@ -76,12 +74,6 @@ if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules))
$modules = DOCROOT.$modules; $modules = DOCROOT.$modules;
} }
// Make the system relative to the docroot, for symlink'd index.php
if ( ! is_dir($sysmodules) AND is_dir(DOCROOT.$sysmodules))
{
$sysmodules = DOCROOT.$sysmodules;
}
// Make the system relative to the docroot // Make the system relative to the docroot
if ( ! is_dir($system) AND is_dir(DOCROOT.$system)) if ( ! is_dir($system) AND is_dir(DOCROOT.$system))
{ {
@ -91,11 +83,10 @@ if ( ! is_dir($system) AND is_dir(DOCROOT.$system))
// Define the absolute paths for configured directories // Define the absolute paths for configured directories
define('APPPATH', realpath($application).DIRECTORY_SEPARATOR); define('APPPATH', realpath($application).DIRECTORY_SEPARATOR);
define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR); define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR);
define('SMDPATH', realpath($sysmodules).DIRECTORY_SEPARATOR);
define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR); define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR);
// Clean up the configuration vars // Clean up the configuration vars
unset($application, $modules, $sysmodules, $system); unset($application, $modules, $system);
/** /**
* Define the start time of the application, used for profiling. * Define the start time of the application, used for profiling.
@ -113,8 +104,6 @@ if ( ! defined('KOHANA_START_MEMORY'))
define('KOHANA_START_MEMORY', memory_get_usage()); define('KOHANA_START_MEMORY', memory_get_usage());
} }
define('PHPUNITTEST','192.168.242.3');
// Bootstrap the application // Bootstrap the application
require APPPATH.'bootstrap'.EXT; require APPPATH.'bootstrap'.EXT;
@ -133,4 +122,4 @@ if (($ob_len = ob_get_length()) !== FALSE)
} }
// Enable the unittest module // Enable the unittest module
Kohana::modules(Kohana::modules() + array('unittest' => SMDPATH.'unittest')); Kohana::modules(Kohana::modules() + array('unittest' => MODPATH.'unittest'));

View File

@ -1,22 +0,0 @@
Copyright (c) 2010 Cédric de Saint Léger
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.

View File

@ -1,31 +0,0 @@
Kohana_XML is a XML modules to generate and read XML documents in Kohana.
It is build for KO3, but there are barely one or two lines that makes it KO3 specific,
so I guess it should work for KO2.x without much trouble.
## Notable Features
* **Extendible, configurable drivers** — You can use the XML class to write simple XML,
or use the Atom driver to generate Atom compliant XML, or write your own driver (extending XML
or another driver) to generate XML compliant to any specs you want. Driver support initial
configuration, which will be used when using native functions, and your own function.
Namespaces and prefix, value filters, default attributes, node name abstraction are all part
of driver configuration and are then used as such by native functions, so they are dealt with
on the fly. But you can also write your own function very easily in your drivers, and writing
an add_author($user_model) function in the Atom driver would take a second.
* **Dealing with objects of the same class whatever function you use** $xml→add_node(“test”);
generates another XML instance of the same driver you can add nodes to, import array or XML files
to, search in, modify, export, combine… The whole XML document becomes modular, easy to read and
to modify, and to run through with method chaining. Just play Lego with your XML.
* **Magic get and get()** — allows to easily run through the document. For instance
$atom→author→name will return an atom document authors name, this regardless of your driver
configuration. As another example of node name abstraction, if youve decided to abstract “pubDate”
with “updated” in your RSS2 driver configuration and “published” with “updated” in you Atom driver,
then $atom→updated will give you the same result as $rss→updated.
* **Jelly-style driver configuration** — I liked the way Jelly initializes its models, so you can
configure yours just the same way. Driver configuration then goes into a static meta class, which
improves performance.
* You can still use **DOM functions** if you wish and reintegrate in Kohana_XML

View File

@ -1,13 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : xml.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* XML class. Use this class to override XML_Core.
* Extend this class to make your own XML based driver (Atom, XRDS, GData, RSS, PodCast RSS, or your own brewed XML format)
*/
class XML extends XML_Core
{}

View File

@ -1,732 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Document : core.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* XML_Core class.
*/
class XML_Core
{
/**
* @var string XML document version
*/
public static $xml_version = "1.0";
/**
* @var string Root Node name
*/
public $root_node;
/**
* The DOM_Element corresponding to this XML instance
* This is made public to use DOM functions directly if desired.
* @var DOM_Element XML instance DOM node.
*/
public $dom_node;
/**
* Basically a handy shortcut of $this->dom_node->ownerDocument
* All XML instance belonging to the same document will have this attribute in common
* @var DOM_Document XML instance DOM document, owner of dom_node
*/
public $dom_doc;
/**
* @var array Array of XML_Meta, containing metadata about XML drivers config
*/
protected static $_metas = array();
/**
* This creates an XML object from the specified driver.
* Specify the driver name, or if there is no specific driver, the root node name
* @param string $driver [optional] Driver Name
* @param string $root_node [optional] Root Node name. Force the root node name. Must be used if no driver nor element is specified.
* @param string $element [optional] XML string or file to generate XML from. Must be used if no driver nor root_node is specified.
* @return XML XML object
*/
public static function factory($driver = NULL, $root_node = NULL, $element = NULL)
{
if ($driver)
{
// Let's attempt to generate a new instance of the subclass corresponding to the driver provided
$class = 'XML_Driver_'.ucfirst($driver);
// Register a new meta object
XML::$_metas[strtolower($class)] = $meta = new XML_Meta;
// Override the meta with driver-specific attributes
call_user_func(array($class, "initialize"), $meta);
// Set content type to default if it is not already set, and report it as initialized
$meta->content_type("text/xml")->set_initialized();
return new $class($element, $root_node);
}
else
{
// Register a new meta object in the root node
XML::$_metas["xml"] = $meta = new XML_Meta;
// Set content type to default if it is not already set, and report it as initialized
$meta->content_type("text/xml")->set_initialized();
return new XML($element, $root_node);
}
}
/**
* Class constructor. You should use the factory instead.
* @param string $element [optional] What to construct from. Could be some xml string, a file name, or a DOMNode
* @param string $root_node [optional] The root node name. To be specified if no driver are used.
* @return XML XML object instance
*/
public function __construct($element = NULL, $root_node = NULL)
{
// Create the initial DOMDocument
$this->dom_doc = new DOMDocument(XML::$xml_version, Kohana::$charset);
if ($root_node)
{
// If a root node is specified, overwrite the current_one
$this->root_node = $root_node;
}
// Initialize the document with the given element
if (is_string($element))
{
if (is_file($element) OR Valid::url($element))
{
// Generate XML from a file
$this->dom_doc->load($element);
}
else
{
// Generate XML from a string
$this->dom_doc->loadXML($element);
}
// Node is the root node of the document, containing the whole tree
$this->dom_node = $this->dom_doc->documentElement;
}
elseif ($element instanceof DOMNode)
{
// This is called from another XML instance ( through add_node, or else...)
// Let's add that node to the new object node
$this->dom_node = $element;
// And overwrite the document with that node's owner document
$this->dom_doc = $this->dom_node->ownerDocument;
}
elseif ( ! is_null($this->root_node))
{
// Create the Root Element from the driver attributes
if ($this->meta()->get("namespace", $this->root_node))
{
$root_node_name = $this->meta()->get("prefix", $this->root_node) ? $this->meta()->get("prefix", $this->root_node).":$this->root_node" : $this->root_node;
// Create the root node in its prefixed namespace
$root_node = $this->dom_doc->createElementNS($this->meta()->get("namespace", $this->root_node), $root_node_name);
}
else
{
// Create the root node
$root_node = $this->dom_doc->createElement($this->root_node);
}
// Append the root node to the object DOMDocument, and set the resulting DOMNode as it's node
$this->dom_node = $this->dom_doc->appendChild($root_node);
// Add other attributes
$this->add_attributes($this->dom_node);
}
else
{
throw new Kohana_Exception("You have to specify a root_node, either in your driver or in the constructor if you're not using any.");
}
}
/**
* Adds a node to the document
* @param string $name Name of the node. Prefixed namespaces are handled automatically.
* @param value $value [optional] value of the node (will be filtered). If value is not valid CDATA,
* it will be wrapped into a CDATA section
* @param array $attributes [optional] array of attributes. Prefixed namespaces are handled automatically.
* @return XML instance for the node that's been added.
*/
public function add_node($name, $value = NULL, $attributes = array())
{
// Trim the name
$name = trim($name);
// Create the element
$node = $this->create_element($name);
// Add the attributes
$this->add_attributes($node, $attributes);
// Add the value if provided
if ($value !== NULL)
{
$value = strval($this->filter($name, $value, $node));
if (str_replace(array('<', '>', '&'), "", $value) === $value)
{
// Value is valid CDATA, let's add it as a new text node
$value = $this->dom_doc->createTextNode($value);
}
else
{
// We shall create a CDATA section to wrap the text provided
$value = $this->dom_doc->createCDATASection($value);
}
$node->appendChild($value);
}
// return a new XML instance of the same class from the child node
$class = get_class($this);
return new $class($this->dom_node->appendChild($node));
}
/**
* Magic get returns the first child node matching the value
* @param string $node_name
* @return mixed If trying to get a node:
* NULL will be return if nothing is matched,
* A string value is returned if it a text/cdata node is matched
* An XML instance is returned otherwise, allowing chaining.
*/
public function __get($value)
{
if ( ! isset($this->$value))
{
$node = current($this->get($value));
if ($node instanceof XML)
{
// Return the whole XML document
return $node;
}
// We did not match any child nodes
return NULL;
}
parent::__get($value);
}
/**
* Gets all nodes matching a name and returns them as an array.
* Can also be used to get a pointer to a particular node and then deal with that node as an XML instance.
* @param string $value name of the nodes desired
* @param bool $as_array [optional] whether or not the nodes should be returned as an array
* @return array Multi-dimensional array or array of XML instances
*/
public function get($value, $as_array = FALSE)
{
$return = array();
$value = $this->meta()->alias($value);
foreach ($this->dom_node->getElementsByTagName($value) as $item)
{
if ($as_array)
{
// Return as array but ignore root node
$array = $this->_as_array($item);
foreach ($array as $val)
{
$return[] = $val;
}
}
else
{
$class = get_class($this);
$return[] = new $class($item);
}
}
return $return;
}
/**
* Queries the document with an XPath query
* @param string $query XPath query
* @param bool $as_array [optional] whether or not the nodes should be returned as an array
* @return array Multi-dimensional array or array of XML instances
*/
public function xpath($query, $as_array = TRUE)
{
$return = array();
$xpath = new DOMXPath($this->dom_doc);
foreach ($xpath->query($query) as $item)
{
if ($as_array)
{
$array = $this->_as_array($item);
foreach ($array as $val)
{
$return[] = $val;
}
}
else
{
$class = get_class($this);
$return[] = new $class($item);
}
}
return $return;
}
/**
* Exports the document as a multi-dimensional array.
* Handles element with the same name.
*
* Root node is ignored, as it is known and available in the driver.
* Example :
* <node_name attr_name="val">
* <child_node_name>
* value1
* </child_node_name>
* <child_node_name>
* value2
* </child_node_name>
* </node_name>
*
* Here's the resulting array structure :
* array ("node_name" => array(
* // array of nodes called "node_name"
* 0 => array(
* // Attributes of that node
* "xml_attributes" => array(
* "attr_name" => "val",
* )
* // node contents
* "child_node_name" => array(
* // array of nodes called "child_node_name"
* 0 => value1,
* 1 => value2,
* )
* The output is retro-actively convertible to XML using from_array().
* @return array
*/
public function as_array()
{
$dom_element = $this->dom_node;
$return = array();
// This function is run on a whole XML document and this is the root node.
// That root node shall be ignored in the array as it driven by the driver and handles document namespaces.
foreach($dom_element->childNodes as $dom_child)
{
if ($dom_child->nodeType == XML_ELEMENT_NODE)
{
// Let's run through the child nodes
$child = $this->_as_array($dom_child);
foreach ($child as $key => $val)
{
$return[$key][]=$val;
}
}
}
return $return;
}
/**
* Recursive as_array for child nodes
* @param DOMNode $dom_node
* @return Array
*/
private function _as_array(DOMNode $dom_node)
{
// All other nodes shall be parsed normally : attributes then text value and child nodes, running through the XML tree
$object_element = array();
// Get the desired node name for this node
$node_name = $this->meta()->key($dom_node->tagName);
// Get children, run through XML tree
if ($dom_node->hasChildNodes())
{
if (!$dom_node->firstChild->hasChildNodes())
{
// Get text value
$object_element[$node_name] = trim($dom_node->firstChild->nodeValue);
}
foreach($dom_node->childNodes as $dom_child)
{
if ($dom_child->nodeType === XML_ELEMENT_NODE)
{
$child = $this->_as_array($dom_child);
foreach ($child as $key=>$val)
{
$object_element[$node_name][$key][]=$val;
}
}
}
}
// Get attributes
if ($dom_node->hasAttributes())
{
$object_element[$dom_node->nodeName]['xml_attributes'] = array();
foreach($dom_node->attributes as $att_name => $dom_attribute)
{
// Get the desired name for this attribute
$att_name = $this->meta()->key($att_name);
$object_element[$node_name]['xml_attributes'][$att_name] = $dom_attribute->value;
}
}
return $object_element;
}
/**
* Converts an array to XML. Expected structure is given in as_array().
* However, from_array() is essentially more flexible regarding to the input array structure,
* as we don't have to bother about nodes having the same name.
* Try something logical, that should work as expected.
* @param object $mixed
* @return XML
*/
public function from_array($array)
{
$this->_from_array($array, $this->dom_node);
return $this;
}
/**
* Array shall be like : array('element_name' => array( 0 => text, 'xml_attributes' => array()));
* @param object $mixed
* @param DOMElement $dom_element
* @return
*/
protected function _from_array($mixed, DOMElement $dom_element)
{
if (is_array($mixed))
{
foreach( $mixed as $index => $mixed_element )
{
if ( is_numeric($index) )
{
// If we have numeric keys, we're having multiple children of the same node.
// Append the new node to the current node's parent
// If this is the first node to add, $node = $dom_element
$node = $dom_element;
if ( $index != 0 )
{
// If not, lets create a copy of the node with the same name
$node = $this->create_element($dom_element->tagName);
// And append it to the parent node
$node = $dom_element->parentNode->appendChild($node);
}
$this->_from_array($mixed_element, $node);
}
elseif ($index == "xml_attributes")
{
// Add attributes to the node
$this->add_attributes($dom_element, $mixed_element);
}
else
{
// Create a new element with the key as the element name.
// Create the element corresponding to the key
$node = $this->create_element($index);
// Add the driver attributes
$this->add_attributes($node);
// Append it
$dom_element->appendChild($node);
// Treat the array by recursion
$this->_from_array($mixed_element, $node);
}
}
}
elseif ($mixed)
{
// This is a string value that shall be appended as such
$mixed = $this->filter($dom_element->tagName, $mixed, $dom_element);
$dom_element->appendChild($this->dom_doc->createTextNode($mixed));
}
}
/**
* This function is used to import another XML instance, or whatever we can construct XML from (string, filename, DOMNode...)
*
* $xml1 = XML::factory("atom", "<feed><bla>bla</bla></feed>");
* $xml2 = XML::factory("rss", "<test></test>");
* $node_xml2 = $xml2->add_node("key");
*
* // outputs "<test><key><feed><bla>bla</bla></feed></key></test>"
* $node_xml2->import($xml1)->render();
*
* // outputs "<feed><bla>bla</bla></feed><key><feed><bla>bla</bla></feed></key>"
* $xml1->import($xml2->get("key"))->render();
*
* @param object $xml XML instance or DOMNode
* @return object $this Chainable function
*/
public function import($xml)
{
if (! $xml instanceof XML)
{
// Attempt to construct XML from the input
$class = get_class($this);
$xml = new $class($xml);
}
// Import the node, and all its children, to the document
$node = $this->dom_doc->importNode($xml->dom_node, TRUE);
$this->dom_node->appendChild($node);
return $this;
}
/**
* Creates an element, sorts out namespaces (default / prefixed)
* @param string $name element name
* @return DOMElement
*/
private function create_element($name)
{
$name = $this->meta()->alias($name);
// Let's check if the element name has a namespace, and if this prefix is defined in our driver
if ($namespace_uri = $this->meta()->get("namespace", $name))
{
if (stristr($name, ":"))
{
// Separate the namespace prefix and the name
list($prefix, $name) = explode(":", $name);
// Register the prefixed namespace in the document root
$this->dom_doc->documentElement->setAttributeNS("http://www.w3.org/2000/xmlns/" ,"xmlns:".$prefix, $namespace_uri);
// Create the prefixed element within that namespace
$node = $this->dom_doc->createElementNS($namespace_uri, $prefix.":".$name);
}
else
{
// Create the element normally
$node = $this->dom_doc->createElement($name);
// Add the new default namespace as an attribute.
$node->setAttribute("xmlns", $namespace_uri);
}
}
else
{
// Simply create the element
$node = $this->dom_doc->createElement($name);
}
return $node;
}
/**
* Applies attributes to a node
* @param DOMNode $node
* @param array $attributes as key => value
* @return DOMNode
*/
private function add_attributes(DOMNode $node, $attributes = array())
{
$node_name = $this->meta()->alias($node->tagName);
if ($this->meta()->get("attributes", $node_name))
{
$attributes = array_merge($this->meta()->get("attributes", $node_name), $attributes);
}
foreach ($attributes as $key => $val)
{
// Trim elements
$key = $this->meta()->alias(trim($key));
$val = $this->filter($key, trim($val), $node);
// Set the attribute
// Let's check if the attribute name has a namespace prefix, and if this prefix is defined in our driver
if ($namespace_uri = $this->meta()->get("namespace", $key)
AND stristr($name, ":"))
{
// Separate the namespace prefix and the name
list($prefix, $name) = explode(":", $name);
// Register the prefixed namespace
$this->dom_node->setAttributeNS("http://www.w3.org/2000/xmlns/" ,"xmlns:".$prefix, $namespace_uri);
// Add the prefixed attribute within that namespace
$node->setAttributeNS($namespace_uri, $key, $val);
}
else
{
// Simply add the attribute
$node->setAttribute($key, $val);
}
}
return $node;
}
/**
* Applies filter on a value.
* These filters are callbacks usually defined in the driver.
* They allow to format dates, links, standard stuff, and play
* as you wish with the value before it is added to the document.
*
* You could even extend it and modify the node name.
*
* @param string $name
* @param string $value
* @return string $value formatted value
*/
protected function filter($name, $value, &$node)
{
$name = $this->meta()->alias($name);
if ($this->meta()->get("filter", $name))
{
return call_user_func(array($this, $this->meta()->get("filter", $name)), $value, $node);
}
return $value;
}
/**
* This is a classic filter that takes a uri and makes a proper link
* @param object $value
* @return $value
*/
public function normalize_uri($value, $node)
{
if (strpos($value, '://') === FALSE)
{
if (strlen(URL::base()) > 1 AND stristr($value, URL::base()))
{
// Make sure the path is not base related
$value = str_replace(URL::base(), '', $value);
}
// Convert URIs to URLs
$value = URL::site($value, TRUE);
}
return $value;
}
/**
* Another classic filter to deal with boolean
* @param boolean $value
* @return string $value, true or false
*/
public function normalize_bool($value)
{
return $value ? "true" : "false";
}
/**
* Returns this drivers XML metadata
* @return XML_Meta
*/
public function meta()
{
return XML::$_metas[strtolower(get_class($this))];
}
/**
* Outputs nicely formatted XML when converting as string
* @return string
*/
public function __toString()
{
return $this->render(TRUE);
}
/**
* Render the XML.
* @param boolean $formatted [optional] Should the output be formatted and indented ?
* @return string
*/
public function render($formatted = FALSE)
{
$this->dom_doc->formatOutput = $formatted;
return $this->dom_doc->saveXML();
}
/**
* Outputs the XML in a file
* @param string filename
* @return
*/
public function export($file)
{
return $this->dom_doc->save($file);
}
/**
* Returns this instance node value, if the dom_node is a text node
*
* @return string
*/
public function value()
{
if ($this->dom_node->hasChildNodes() AND $this->dom_node->firstChild->nodeType === XML_TEXT_NODE)
{
return $this->dom_node->nodeValue;
}
return NULL;
}
/**
* Returns this instance node value
*
* @return string|array attributes as array of attribute value if a name is specified
*/
public function attributes($attribute_name = NULL)
{
if ($attribute_name === NULL)
{
// Return an array of attributes
$attributes = array();
if ($this->dom_node->hasAttributes())
{
foreach ($this->dom_node->attributes as $attribute)
{
$attributes[$attribute->name] = $attribute->value;
}
}
return $attributes;
}
// Simply return the attribute value
return $this->dom_node->getAttribute($attribute_name);
}
} // End XML_Core

View File

@ -1,101 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : atom.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* Atom driver
*/
class XML_Driver_Atom extends XML
{
public $root_node = 'feed';
protected static function initialize(XML_Meta $meta)
{
$meta ->content_type("application/atom+xml")
->nodes (
array(
"feed" => array("namespace" => "http://www.w3.org/2005/Atom"),
// "entry" => array("namespace" => "http://www.w3.org/2005/Atom"),
"href" => array("filter" => "normalize_uri"),
"link" => array("filter" => "normalize_uri"),
"logo" => array("filter" => "normalize_uri"),
"icon" => array("filter" => "normalize_uri"),
"id" => array("filter" => "normalize_uri"),
"updated" => array("filter" => "normalize_datetime"),
"published" => array("filter" => "normalize_datetime"),
"startDate" => array("filter" => "normalize_date"),
'endDate' => array("filter" => "normalize_date"),
"summary" => array("filter" => "normalize_text"),
"subtitle" => array("filter" => "normalize_text"),
"title" => array("filter" => "normalize_text"),
"content" => array("filter" => "normalize_text")
)
);
}
public function add_person($type, $name, $email = NULL, $uri = NULL)
{
$author = $this->add_node($type);
$author->add_node("name", $name);
if ($email)
{
$author->add_node("email", $email);
}
if ($uri)
{
$author->add_node("uri", $uri);
}
return $this;
}
public function add_content(XML $xml_document)
{
$this->add_node("content", NULL, array("type" => $xml_document->meta()->content_type()))->import($xml_document);
return $this;
}
public function normalize_text($value, $node)
{
if (strpos($value, "<") >= 0 AND strpos($value, ">") > 0)
{
// Assume type = html
$node->setAttribute("type", "html");
}
else
{
$node->setAttribute("type", "text");
}
return $value;
}
public function normalize_datetime($value)
{
if ( ! is_numeric($value))
{
$value = strtotime($value);
}
// Convert timestamps to RFC 3339 formatted datetime
return date(DATE_RFC3339, $value);
}
public function normalize_date($value)
{
if ( ! is_numeric($value))
{
$value = strtotime($value);
}
// Convert timestamps to RFC 3339 formatted dates
return date("Y-m-d", $value);
}
}

View File

@ -1,57 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : rss2.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* RSS2 driver
*/
class XML_Driver_Rss2 extends XML
{
public $root_node = 'rss';
protected static function initialize(XML_Meta $meta)
{
$meta ->content_type("application/rss+xml")
->nodes (
array(
"rss" => array("attributes" => array("version" => "2.0")),
"title" => array("filter" => "normalize_text"),
"description" => array("filter" => "normalize_text"),
"link" => array("filter" => "normalize_uri"),
"atom:link" => array("attributes" => array(
"rel" => "self",
"type" => "application/rss+xml",
// "href" => URL::site(Request::initial()->uri(), TRUE)
),
"namespace" => "http://www.w3.org/2005/Atom"),
"href" => array("filter" => "normalize_uri"),
"docs" => array("filter" => "normalize_uri"),
"guid" => array("filter" => "normalize_uri"),
"pubDate" => array("filter" => "normalize_date"),
"lastBuildDate" => array("filter" => "normalize_date")
)
);
}
public function normalize_date($value)
{
if ( ! is_numeric($value))
{
$value = strtotime($value);
}
// Convert timestamps to RFC 822 formatted dates, with 4 digits year
return date(DATE_RSS, $value);
}
public function normalize_text($value)
{
// Strip HTML tags
return strip_tags($value);
}
}

View File

@ -1,55 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : xrds.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* XRDS driver. For Service Discovery.
*/
class XML_Driver_XRDS extends XML
{
public $root_node = 'xrds:XRDS';
protected static function initialize(XML_Meta $meta)
{
$meta ->content_type("application/xrds+xml")
->nodes (
array(
"xrds:XRDS" => array("namespace" => 'xri://$xrds', "attributes" => array("xmlns" => 'xri://$xrd*($v*2.0)')),
"LocalID" => array("filter" => "normalize_uri"),
"openid:Delegate" => array("filter" => "normalize_uri", "namespace" => "http://openid.net/xmlns/1.0"),
"URI" => array("filter" => "normalize_uri"),
)
);
}
public function add_service($type, $uri, $priority = NULL)
{
if (! is_null($priority))
{
$priority = array("priority" => $priority);
}
else
{
$priority = array();
}
$service_node = $this->add_node("Service", NULL, $priority);
if (! is_array($type))
{
$type = array($type);
}
foreach ($type as $t)
{
$service_node->add_node("Type", $t);
}
$service_node->add_node("URI", $uri);
return $service_node;
}
}

View File

@ -1,12 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : meta.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* XML_Meta class. Use this to override XML_Meta_Core
*/
class XML_Meta extends XML_Meta_Core
{}

View File

@ -1,187 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Document : meta.php
* Created on : 1 mai 2009, 13:03:03
* @author Cedric de Saint Leger <c.desaintleger@gmail.com>
*
* Description:
* XML_Meta_Core class. This class contains XML drivers metadata
*/
class XML_Meta_Core
{
/**
* @var array assoc array alias => $node_name
* This is used to abstract the node names
*/
protected $nodes = array();
/**
* @var array whole node configuration array
* array("node_name" => array(
* // Effective node name in the XML document.
* // This name is abstracted and "node_name" is always used when dealing with the object.
* "node" => "effective_node_name",
* // Defines a namespace URI for this node
* "namespace" => "http://www.namespace.uri",
* // Defines a prefix for the namespace above. If not defined, namespace is interpreted as a default namespace
* "prefix" => "ns",
* // Defines a callback function to filter/normalize the value
* "filter" => "filter_function_name",
* // Array of attributes
* "attributes" => array("default_attribute1" => "value")
* ),
* "alias" => "node_name",
* )
*/
protected $nodes_config = array();
/**
* @var string content type for HTML headers
*/
protected $content_type;
/**
* @var boolean whether the object is initialized
*/
protected $_initialized = FALSE;
/**
* Returns the name of a node, sort out aliases
* @param string $name
* @return string $node_name
*/
public function alias($name)
{
if (isset($this->nodes_config[$name]))
{
if ( ! is_array($this->nodes_config[$name]))
{
$name = $this->nodes_config[$name];
}
}
return Arr::get($this->nodes, $name, $name);
}
/**
* Returns the value of a meta key for a given node name
* exemple $this->get('attributes', 'feed') will return all the attributes set up in the meta
* for the node feed.
* @param object $key meta key
* @param object $name node name
* @return meta value or NULL if not set
*/
public function get($key, $name)
{
$name = $this->alias($name);
if (isset($this->nodes_config[$name]) AND is_array($this->nodes_config[$name]) AND array_key_exists($key, $this->nodes_config[$name]))
{
return $this->nodes_config[$name][$key];
}
return NULL;
}
/**
* Set nodes config attribute
* Use it this way :
* nodes(array("node_name" => array("namespace" => "http://www.namespace.uri", "prefix" => "ns", "filter" => "filter_function_name", "attributes" => array("default_attribute1" => "value")))),
* OR to set up node alias names :
* nodes(array("alias" => "node_name"));
*
* @param array $nodes array formatted as mentionned above
* @param bool $overwrite [optional] Overwrite current values if they are set ?
* @return object $this
*/
public function nodes(Array $nodes)
{
$this->nodes_config = $this->_initialized ?
array_merge($nodes, $this->nodes_config) :
array_merge($this->nodes_config, $nodes);
$this->generate_nodes_map();
return $this;
}
/**
* Sets the content type for headers
* @param string $type
* @return object $this
*/
public function content_type($type = NULL)
{
if ($type)
{
$this->content_type = $this->_initialized ?
$type :
$this->content_type ?
$this->content_type :
$type;
}
else
{
return $this->content_type;
}
return $this;
}
/**
* Returns the key name corresponding to a node name
* This is used when using as_array(), to return array keys corresponding to the node names
* @param object $node_name
* @return
*/
public function key($node_name)
{
// Extract the name if it is prefixed
$expl = explode(":", $node_name);
$node_name = count($expl) > 1 ? end($expl) : current($expl);
if (in_array($node_name, $this->nodes))
{
return current(array_keys($this->nodes, $node_name));
}
return $node_name;
}
/**
* Generates - or re-generates the node map
* @return object $this
*/
public function generate_nodes_map()
{
$map = array();
foreach ($this->nodes_config as $key => $config)
{
if (is_array($config))
{
if (isset ($config["node"]))
{
$map[$key] = $config["node"];
}
}
}
$this->nodes = $map;
return $this;
}
/**
* Reports the Meta as initialized.
* This basically allows Meta methods to overwrite existing value, if they are called explicitely
* @return object $this
*/
public function set_initialized()
{
$this->_initialized = TRUE;
return $this;
}
}

View File

@ -252,17 +252,13 @@ class Kohana_Debug {
{ {
$file = 'APPPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(APPPATH)); $file = 'APPPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(APPPATH));
} }
elseif (strpos($file, MODPATH) === 0)
{
$file = 'MODPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(MODPATH));
}
elseif (strpos($file, SYSPATH) === 0) elseif (strpos($file, SYSPATH) === 0)
{ {
$file = 'SYSPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SYSPATH)); $file = 'SYSPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SYSPATH));
} }
elseif (strpos($file, SMDPATH) === 0) elseif (strpos($file, MODPATH) === 0)
{ {
$file = 'SMDPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SMDPATH)); $file = 'MODPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(MODPATH));
} }
elseif (strpos($file, DOCROOT) === 0) elseif (strpos($file, DOCROOT) === 0)
{ {

View File

@ -64,7 +64,7 @@ class Kohana_Request_Client_Curl extends Request_Client_External {
$this->_options[CURLOPT_HEADER] = FALSE; $this->_options[CURLOPT_HEADER] = FALSE;
// Apply any additional options set to // Apply any additional options set to
$options = Arr::merge($options, $this->_options); $options += $this->_options;
$uri = $request->uri(); $uri = $request->uri();

View File

@ -250,7 +250,7 @@ class Kohana_CoreTest extends Unittest_TestCase
{ {
return array( return array(
array(array('unittest' => MODPATH.'fo0bar')), array(array('unittest' => MODPATH.'fo0bar')),
array(array('unittest' => SMDPATH.'unittest', 'fo0bar' => MODPATH.'fo0bar')), array(array('unittest' => MODPATH.'unittest', 'fo0bar' => MODPATH.'fo0bar')),
); );
} }
@ -292,7 +292,7 @@ class Kohana_CoreTest extends Unittest_TestCase
{ {
return array( return array(
array(array(), array()), array(array(), array()),
array(array('unittest' => SMDPATH.'unittest'), array('unittest' => $this->dirSeparator(SMDPATH.'unittest/'))), array(array('unittest' => MODPATH.'unittest'), array('unittest' => $this->dirSeparator(MODPATH.'unittest/'))),
); );
} }

View File

@ -59,8 +59,8 @@ class Kohana_DebugTest extends Unittest_TestCase
'SYSPATH'.DIRECTORY_SEPARATOR.'classes'.DIRECTORY_SEPARATOR.'kohana.php' 'SYSPATH'.DIRECTORY_SEPARATOR.'classes'.DIRECTORY_SEPARATOR.'kohana.php'
), ),
array( array(
SMDPATH.$this->dirSeparator('unittest/classes/kohana/unittest/runner').EXT, MODPATH.$this->dirSeparator('unittest/classes/kohana/unittest/runner').EXT,
$this->dirSeparator('SMDPATH/unittest/classes/kohana/unittest/runner').EXT $this->dirSeparator('MODPATH/unittest/classes/kohana/unittest/runner').EXT
), ),
); );
} }