Update from AER.NEW
This commit is contained in:
parent
b65ee7b736
commit
6e433c574d
@ -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
|
@ -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 {}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
);
|
@ -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();
|
||||
}
|
@ -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
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -92,7 +92,7 @@ class Kohana_Minion_CLI {
|
||||
{
|
||||
foreach ($values as $opt => $value)
|
||||
{
|
||||
if ( ! in_array($opt, $options, TRUE))
|
||||
if ( ! in_array($opt, $options))
|
||||
{
|
||||
// Set the given value
|
||||
unset($values[$opt]);
|
||||
|
@ -183,15 +183,6 @@ class Kohana_ORM extends Model implements serializable {
|
||||
*/
|
||||
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?
|
||||
* @var bool
|
||||
@ -277,7 +268,7 @@ class Kohana_ORM extends Model implements serializable {
|
||||
else
|
||||
{
|
||||
// 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))
|
||||
@ -360,7 +351,6 @@ class Kohana_ORM extends Model implements serializable {
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
@ -369,7 +359,7 @@ class Kohana_ORM extends Model implements serializable {
|
||||
{
|
||||
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;
|
||||
@ -636,7 +626,7 @@ class Kohana_ORM extends Model implements serializable {
|
||||
$model = $this->_related($column);
|
||||
|
||||
// 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']];
|
||||
|
||||
// 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);
|
||||
|
||||
if (! is_array($this->_has_one[$column]['foreign_key']))
|
||||
{
|
||||
// Use this model's primary key value and foreign model's column
|
||||
$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);
|
||||
}
|
||||
}
|
||||
// Use this model's primary key value and foreign model's column
|
||||
$col = $model->_object_name.'.'.$this->_has_one[$column]['foreign_key'];
|
||||
$val = $this->pk();
|
||||
|
||||
$model->where($col, '=', $val)->find();
|
||||
|
||||
@ -679,52 +655,29 @@ class Kohana_ORM extends Model implements serializable {
|
||||
{
|
||||
$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_col1 = ($this->_disable_join_table_name ? '' : $through.'.').$this->_has_many[$column]['far_key'];
|
||||
$join_col2 = ($this->_disable_join_table_name ? '' : $model->_object_name.'.').$model->_primary_key;
|
||||
// Join on through model's target foreign key (far_key) and target model's primary key
|
||||
$join_col1 = $through.'.'.$this->_has_many[$column]['far_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
|
||||
$col = ($this->_disable_join_table_name ? '' : $through.'.').$this->_has_many[$column]['foreign_key'];
|
||||
$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);
|
||||
// Through table's source foreign key (foreign_key) should be this model's primary key
|
||||
$col = $through.'.'.$this->_has_many[$column]['foreign_key'];
|
||||
$val = $this->pk();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this->_has_many[$column]['foreign_key'] as $mk => $fk)
|
||||
{
|
||||
if (isset($this->_has_many[$column]['through']))
|
||||
{
|
||||
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;
|
||||
// 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'];
|
||||
$val = $this->pk();
|
||||
}
|
||||
|
||||
return $model->where($col, '=', $val);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1078,15 +1031,14 @@ class Kohana_ORM extends Model implements serializable {
|
||||
{
|
||||
$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
|
||||
$this->_db_builder->limit(1);
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
@ -1095,7 +1047,7 @@ class Kohana_ORM extends Model implements serializable {
|
||||
if (strpos($column, '.') === FALSE)
|
||||
{
|
||||
// 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);
|
||||
@ -1213,10 +1165,9 @@ class Kohana_ORM extends Model implements serializable {
|
||||
* @param string $value The value to filter
|
||||
* @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
|
||||
$wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE];
|
||||
|
@ -15,15 +15,13 @@ $application = 'application';
|
||||
*/
|
||||
$modules = 'modules';
|
||||
|
||||
$sysmodules = 'includes/kohana/modules';
|
||||
|
||||
/**
|
||||
* The directory in which the Kohana resources are located. The system
|
||||
* directory must contain the classes/kohana.php file.
|
||||
*
|
||||
* @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
|
||||
@ -76,12 +74,6 @@ if ( ! is_dir($modules) AND is_dir(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
|
||||
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('APPPATH', realpath($application).DIRECTORY_SEPARATOR);
|
||||
define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR);
|
||||
define('SMDPATH', realpath($sysmodules).DIRECTORY_SEPARATOR);
|
||||
define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR);
|
||||
|
||||
// Clean up the configuration vars
|
||||
unset($application, $modules, $sysmodules, $system);
|
||||
unset($application, $modules, $system);
|
||||
|
||||
/**
|
||||
* 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('PHPUNITTEST','192.168.242.3');
|
||||
|
||||
// Bootstrap the application
|
||||
require APPPATH.'bootstrap'.EXT;
|
||||
|
||||
@ -133,4 +122,4 @@ if (($ob_len = ob_get_length()) !== FALSE)
|
||||
}
|
||||
|
||||
// Enable the unittest module
|
||||
Kohana::modules(Kohana::modules() + array('unittest' => SMDPATH.'unittest'));
|
||||
Kohana::modules(Kohana::modules() + array('unittest' => MODPATH.'unittest'));
|
@ -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.
|
@ -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 author’s name, this regardless of your driver
|
||||
configuration. As another example of node name abstraction, if you’ve 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
|
@ -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
|
||||
{}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
{}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -252,17 +252,13 @@ class Kohana_Debug {
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ class Kohana_Request_Client_Curl extends Request_Client_External {
|
||||
$this->_options[CURLOPT_HEADER] = FALSE;
|
||||
|
||||
// Apply any additional options set to
|
||||
$options = Arr::merge($options, $this->_options);
|
||||
$options += $this->_options;
|
||||
|
||||
$uri = $request->uri();
|
||||
|
||||
@ -132,4 +132,4 @@ class Kohana_Request_Client_Curl extends Request_Client_External {
|
||||
return $options;
|
||||
}
|
||||
|
||||
} // End Kohana_Request_Client_Curl
|
||||
} // End Kohana_Request_Client_Curl
|
@ -250,7 +250,7 @@ class Kohana_CoreTest extends Unittest_TestCase
|
||||
{
|
||||
return array(
|
||||
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(
|
||||
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/'))),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -59,8 +59,8 @@ class Kohana_DebugTest extends Unittest_TestCase
|
||||
'SYSPATH'.DIRECTORY_SEPARATOR.'classes'.DIRECTORY_SEPARATOR.'kohana.php'
|
||||
),
|
||||
array(
|
||||
SMDPATH.$this->dirSeparator('unittest/classes/kohana/unittest/runner').EXT,
|
||||
$this->dirSeparator('SMDPATH/unittest/classes/kohana/unittest/runner').EXT
|
||||
MODPATH.$this->dirSeparator('unittest/classes/kohana/unittest/runner').EXT,
|
||||
$this->dirSeparator('MODPATH/unittest/classes/kohana/unittest/runner').EXT
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user