Skip to content

Instantly share code, notes, and snippets.

@timvisee
Created May 3, 2015 16:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timvisee/28bdf2a4872dea03af94 to your computer and use it in GitHub Desktop.
Save timvisee/28bdf2a4872dea03af94 to your computer and use it in GitHub Desktop.
########################################################################
# #
# .htaccess #
# Default .htaccess file for Carbon CMS. #
# @author Tim Visee #
# @website http://timvisee.com/ #
# @copyright Copyright © Tim Visee 2012-2013, All rights reserved. #
# #
########################################################################
# Enable mod rewrite
Options +FollowSymLinks
RewriteEngine On
# Rewrite some conditions
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
# Route every request to the correct index.php page
RewriteRule ^(.*)$ index.php/$1 [L]
# This file has automaticly been generated by Carbon CMS.
#
# This .htaccess file used to protect the cache folder against hackers and other security issues.
# Do not remove this file!
deny from all
##########################################################################
# #
# .htaccess #
# Default .htaccess file for Carbon CMS. #
# @author Tim Visee #
# @website http://timvisee.com/ #
# @copyright Copyright (c) Tim Visee 2012-2014, All rights reserved. #
# #
##########################################################################
# Deny all direct requests to this directory
Deny From All
<?php
/**
* Bootstrap.php
*
* The Bootstrap class constructs all the basic classes like the Database and the Config class.
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\cms;
use carbon\core\cache\CacheHandler;
use carbon\core\config\ConfigHandler;
use carbon\core\Core;
use carbon\core\database\driver\mysql\Database;
use carbon\core\database\driver\mysql\DatabaseStatement;
use carbon\core\datetime\DateTime;
use carbon\core\datetime\interval\DateInterval;
use carbon\core\datetime\interval\spec\DateIntervalSpecUtils;
use carbon\core\ErrorHandler;
use carbon\core\EventManager;
use carbon\core\io\filesystem\directory\Directory;
use carbon\core\io\filesystem\directory\DirectoryScanner;
use carbon\core\io\filesystem\file\File;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\language\util\LanguageTagUtils;
use carbon\core\Listener;
use carbon\core\log\Logger;
use carbon\core\PluginManager;
use carbon\core\RegistryHandler;
use carbon\core\Router;
use carbon\core\UserManager;
use carbon\core\util\DateUtils;
use carbon\core\util\IpUtils;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CMS_INIT') or die('Access denied!');
/**
* Constructs all the basic classes like the Database and the Config class.
* @package core
* @author Tim Visee
*/
class Bootstrap {
/**
* Constructor
* @param bool $init True to initialize the bootstrap immediately
*/
public function __construct($init = true) {
// Initialize the bootstrap
if($init)
$this->init();
}
/**
* Initialize the Bootstrap
*/
public function init() {
// Initialize and load the configuration handler and set the configuration handler instance in the Core class
$cfg = new ConfigHandler(new FilesystemObject(CARBON_CMS_CONFIG));
Core::setConfig($cfg);
// Set up and initialize the error handler
ErrorHandler::init(true, true, true); // TODO: Read this debug property from the configuration file!
/**
**
**
** TEST CODE START
**
**
*/
$a = DateTime::now();
$b = $a->copy()->addDays(10)->addHours(10);
$diff = DateTime::now()->diff(DateTime::now()->addDays(2)->addHours(2));
$interval = DateInterval::createFromDateString('-3 days');
echo '[Test] Interval: ' . $interval->toString() . ' ' . ($interval->isInverted() ? '(-)' : '(+)') . '<br />';
$spec = 'P1WT1H';
$test = new DateInterval(new \DateInterval($spec));
//$test->setHours(-1);
echo '[Test] Is valid DateInterval specification (' . $spec . '): ' . (DateIntervalSpecUtils::isValid($spec) ? 'true' : 'false') . '<br />';
echo '[Test] Parsed interval result: ' . $test->toString() . '<br />';
// Validate the DateInterval:isValidSpec(...); method
/*$tz = DateTimeZoneUtils::parse('Europe/London');
var_dump($tz->getTransitions(DateTime::parse('-100 year')->getTimestamp(), DateTime::now()->getTimestamp()));*/
$dt = new DateTime();
$dt->addYear(5);
echo '[Test] The date 5 years in the future: ' . $dt->toString() . '<br />';
// Set up the logger
$log = new Logger('log.txt');
$log->setPrintLogs(true);
Core::setLogger($log);
// TODO: Check if overwriting works with ::rename();
// TODO: Add method to Directory class which checks if the directory contains any file system objects.
$file = new File('log.txt');
//echo 'READ FROM FILE:<br>' . $file->getContents();
//FileHelper::prepend($file, 'prepend test');
// Log a test message
$log->debug('Request at: ' . DateTime::now()->toCompleteString());
$dir = new Directory(CARBON_SITE_ROOT . '/');
$scanner = new DirectoryScanner($dir, true);
$scanner->readAll();
$statement = new DatabaseStatement();
$val1 = 0;
$val2 = 38;
$val3 = 'test';
die();
$statement->bindParamValue(Array('p', 'test', '3'), Array($val1, $val2, $val3));
//echo 'PARAMS<br />------<br />';
//print_r($statement->getParams());
//echo '<br /><br />';
/*$db = new DatabaseQueryBuilderSelect();
$db->setFrom(Array("tbl1", "tbl2", "tbl3"));
echo "Query: " . $db->getQuery() . '<br /><br /><br />';*/
/*$database = new DatabaseConnector('127.0.0.1', 3306, 'root', 'hoihoi', 'carbon_cms', true);
if($database->isConnected())
echo 'Connected to database!';
else
echo 'ERROR: NOT Connected to database!';*/
//die('</pre>');
/*$tag = "q\n\r= 0.89";
$preg = '/^\statements*q\statements*=\statements*(1(\.0+)?|0\.[0-9]+)$/';
if(preg_match($preg, $tag))
die("(" . $preg . " --- " . $tag . ") Good!");
die("(" . $preg . " --- " . $tag . ") Wrong!");*/
// Initialize the language manager
/*echo "Known Languages:<br /><br /><pre>";
print_r(LanguageUtils::getLanguages());
echo '</pre>';
die();
echo 'Locales:<br /><br /><pre>';
print_r(LanguageTagUtils::getLocales(true));
echo '</pre>';
die();*/
LanguageTagUtils::isValidTag('');
/**
**
**
** TEST CODE END
**
**
*/
// TODO: Should we store the Bootstrap instance in the Core class?
// TODO: Enable or disable the debug mode in the error handler, based on the configuration file
// TODO: Try to enable this earlier
// TODO: Build better debugging system!
// Should the debug mode be enabled
if($cfg->getBool('carbon', 'debug', false) === true) {
// Enable the debug mode in the Error Handler
ErrorHandler::setDebug(true);
// Enable PHPs debugging mode
ini_set('display_errors', 'On');
error_reporting(E_ALL);
} else {
// Disable the debug mode in the Error Handler
ErrorHandler::setDebug(false);
// TODO: Do this in bootstrap shutdown method?
// Turn off the debugging mode (might still be enabled)
ini_set('display_errors', 'Off');
}
// Initialize the router and set the router instance in the Core class
$router = new Router();
Core::setRouter($router);
// Set up the database and set the database instance in the Core class
$database = $this->setUpDatabase();
Core::setDatabase($database);
// Set up the registry handler class and set the registry handler in the Core class
$options = new RegistryHandler(Core::getDatabase());
Core::setRegistry($options);
// Set up the caching system and set the cache instance in the Core class
$cache = $this->setUpCache();
Core::setCache($cache);
// Set the cache instance in the RegistryHandler class
$options->setCache($cache);
// Set up and initialize the user manager and set the user manager instance in the Core class
$user_man = new UserManager($cache, $database);
Core::setUserManager($user_man);
// Set the default timezone of the server
$this->setServerTimezone();
// Initialize the event manager and set the event manager instance in the Core class
$event_man = new EventManager();
Core::setEventManager($event_man);
// Set up the plugin manager and set the plugin manager instance in the Core class
$plugin_man = $this->setUpPluginManager();
Core::setPluginManager($plugin_man);
// Verify the client requesting the page, make sure this client was not banned
$this->verifyClient();
// Route the page request to through the router to the right controller
$router->route();
}
// TODO: Move this method to the Core class
/**
* Set up and initialize the database system
* @return Database Database instance
*/
public function setUpDatabase() {
// Get the ConfigHandler instance
$cfg = Core::getConfig();
// Retrieve the database connection details from the config
$db_host = $cfg->getValue('database', 'host');
$db_port = $cfg->getValue('database', 'port');
$db_database = $cfg->getValue('database', 'database');
$db_username = $cfg->getValue('database', 'username');
$db_password = $cfg->getValue('database', 'password');
// Get the database table prefix
$table_prefix = $cfg->getValue('database', 'table_prefix', '');
// Construct the database class
$db = new Database($table_prefix);
// TODO: Error handling for wrong database credentials and similar stuff
// Connect to the database, try to reconnect if failed
try {
// Try to connect to the database
$db->connectDatabase($db_host, $db_port, $db_database, $db_username, $db_password);
} catch(\PDOException $ex) {
// The connection to the database failed, try to connect once again
try {
// Try to connect to the database
$db->connectDatabase($db_host, $db_port, $db_database, $db_username, $db_password);
} catch(\PDOException $ex) {
// The connection to the database failed twice, show an error message
// TODO: Show proper error message
die('Failed to connect to the database!<br />' . $ex->getMessage());
}
}
// Return the database instance
return $db;
}
// TODO: Move this method to the Core class
/**
* Set up and initialize the caching system
* @return CacheHandler Cache instance
*/
public function setUpCache() {
// Get the RegistryHandler instance
$options = Core::getRegistry();
// Get the cache dir to use
$cache_dir = CARBON_SITE_ROOT . DIRECTORY_SEPARATOR . ltrim($options->getString('cache.directory', DIRECTORY_SEPARATOR . 'cache'), '/\\');
// Initialize the caching system
$cache = new CacheHandler($cache_dir);
// Set if cache is enabled or not
$cache->setEnabled($options->getBool('cache.enabled', true));
// Return the cache instance
return $cache;
}
// TODO: Move this method to the Core class
/**
* Set up and initialize the plugin manager
* @return PluginManager Instance of the plugin manager
*/
public function setUpPluginManager() {
// Get the plugins directory path from the registry
$plugins_dir = CARBON_SITE_ROOT . DIRECTORY_SEPARATOR . ltrim(Core::getRegistry()->getString('plugin.directory', DIRECTORY_SEPARATOR . 'plugin'), '/\\');
// Initialize/construct the Module Manager and store it in a variable
$plugin_mngr = new PluginManager($plugins_dir, Core::getEventManager(), Core::getCache(), Core::getDatabase());
// Load and enable all plugins
$plugin_mngr->loadPlugins();
$plugin_mngr->enablePlugins();
// Return the plugin manager instance
return $plugin_mngr;
}
// TODO: Should this method be moved to the core class?
/**
* Set the default timezone of the server
*/
public function setServerTimezone() {
// Retrieve the default timezone from the Options database and trim the value
$timezone = trim(Core::getRegistry()->getString("time.def_timezone", ""));
// If the timezone is valid, set the timezone of the server
if($timezone != null)
if(DateUtils::isValidTimezone($timezone))
DateUtils::setTimezone($timezone);
}
// TODO: Move this method to a utilities class?
/**
* Verify the client requesting the page
*/
public function verifyClient() {
// Check if the IP of the client is unknown or not
if(IpUtils::isClientIpUnknown()) {
// TODO: Check if clients from unknown IP'statements are blocked or not
// TODO: Check for country, hosting provider, check if is proxy, etc...
} else {
// Get the IP address of the client
//$client_ip = IpUtils::getClientIp();
// TODO: Check if the IP address of the client is blocked
/*if(fsockopen($client_ip, 80, $errstr, $errno, 1)) {
die("Proxy access not allowed");
}*/
}
// TODO: Should the client language be verified?
// TODO: Check if proxies should be blocked
// TODO: Show info message when using site through localhost
}
/**
* Stop the Bootstrap, should only be called after the Bootstrap has been initialized.
*/
// TODO: Rename these methods to start and stop?
public function shutdown() {
// Get the PluginsManager instance
$plugin_mngr = Core::getPluginManager();
// Disable/shutdown all running plugins
if($plugin_mngr != null)
$plugin_mngr->disablePlugins();
// TODO: Unregister all registered events (probably already done!)
// TODO: Deinitialize all classes if required
// TODO: Disconnect from database
// TODO: Other shutdown stuff here...
// TODO: Disable PHPstatements debug stuff?
}
}
<?php
// Make sure the current PHP version is supported
if(version_compare(phpversion(), '5.3.1', '<'))
// PHP version the server is running is not supported, show an error message
// TODO: Show proper error message
die('This server is running PHP ' . phpversion() . ', the required PHP version to start Carbon CMS is PHP 5.3.1 or higher,
please install PHP 5.3.1 or higher on your server!');
// Prevent direct requests to this file due to security reasons
defined('CARBON_SITE_ROOT') or die('Access denied!');
// Make sure Carbon CMS isn't initialized already
if(defined('CARBON_CMS_INIT'))
if(CARBON_CMS_INIT === true)
return;
// Define some Carbon CMS constants
/** Defines the root directory for Carbon CMS */
define('CARBON_CMS_ROOT', __DIR__);
/** Define the version code of the current installed Carbon CMS instance */
define('CARBON_CMS_VERSION_CODE', 1);
/** Define the version name of the current installed Carbon CMS instance */
define('CARBON_CMS_VERSION_NAME', '0.1');
/** Defines the file path of the Carbon CMS configuration file */
define('CARBON_CMS_CONFIG', CARBON_SITE_ROOT . '/config/config.php');
// Set the configuration file for Carbon Core
// TODO: Improve the quality of this code part bellow!
/** Defines the file path of the Carbon Core configuration file */
define('CARBON_CORE_CONFIG', CARBON_CMS_CONFIG);
// Load and initialize Carbon Core
require(CARBON_SITE_ROOT . '/carbon/core/init.php');
// Make sure Carbon Core initialized successfully
if(defined('CARBON_CORE_INIT'))
if(CARBON_CORE_INIT !== true)
return;
// Load, initialize and set up the autoloader
require(CARBON_CORE_ROOT . '/Autoloader.php');
use carbon\core\Autoloader;
Autoloader::initialize(CARBON_SITE_ROOT);
Autoloader::registerNamespace("carbon\\core", CARBON_CORE_ROOT);
Autoloader::registerNamespace("carbon\\cms", CARBON_SITE_ROOT);
// Carbon CMS initialized successfully, define the CARBON_CMS_INIT constant to store the initialization state
/** Defines whether Carbon CMS is initialized successfully */
define('CARBON_CMS_INIT', true);
<?php
// TODO: Improve the performance of the autoloader where possible!
// TODO: Feature that allows the registration of custom methods to load classes, for a more dynamic auto loader.
/**
* Autoloader.php
* The Autoloader class which takes care of all non-loaded classes and tries to loadLocalesList them when being used.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2012-2014. All rights reserved.
*/
namespace carbon\core;
use carbon\core\util\ArrayUtils;
use carbon\core\util\StringUtils;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Autoloader class
*
* @package carbon\core
* @author Tim Visee
*/
class Autoloader {
/**
* @var string $root Root directory to load all classes from by default.
* Is set to the Carbon Core root directory by default.
*/
private static $root = CARBON_CORE_ROOT;
/**
* @var bool Defines whether the autoloader has been initialized.
* True if the autoloader has been initialized, false otherwise.
*/
private static $init = false;
/** @var array $regClasses Array containing all registered classes */
private static $regClasses = Array();
/** @var array $regNamespaces Array containing all registered namespaces */
private static $regNamespaces = Array();
/** @var array $regExtensions Array containing all registered extensions */
private static $regExtensions = Array('.php');
/**
* Initialize the Autoloader, the autoloader may not be initialized yet.
*
* @param string|null $root Root path, to load all classes from by default. Null to use the default.
*
* @return bool True if succeed, false otherwise.
* True will also be returned if the autoloader was initialized before.
*/
public static function initialize($root = null) {
// Make sure the autoloader hasn't been initialized yet
if(self::isInitialized())
return true;
// Register the auto loader method
// TODO: Check whether this method should throw errors or not, probably yes before the error handler will take care of everything.
if(spl_autoload_register(__CLASS__ . '::loadClass', false, true) === false)
return false;
// Set the root path
// TODO: Use File instances, if possible!
if($root !== null)
self::setRoot($root);
// Make sure the .php extension is registered by default
self::registerExtension('.php', true);
// Set the initialization state, return the result
self::$init = true;
return true;
}
/**
* Check whether the autoloader has been initialized.
*
* @return bool True if the autoloader has been initialized, false otherwise.
*/
public static function isInitialized() {
return self::$init;
}
/**
* Finalize the autoloader, the autoloader must be initialized.
*
* @param bool $keepConfig True to keep the current root directory, classes, namespaces and extensions
* configuration, false to clear this configuration.
*
* @return bool True on success, false on failure.
*/
public static function finalize($keepConfig = false) {
// Make sure the autoloader is initialized
if(!self::isInitialized())
return true;
// Unregister the autoloader function
if(spl_autoload_unregister(__CLASS__ . '::loadClass') === false)
return false;
// Check whether we should clear the current configuration
if(!$keepConfig) {
self::$root = CARBON_CORE_ROOT;
self::$regClasses = Array();
self::$regNamespaces = Array();
self::$regExtensions = Array();
}
// Set the initialization state, return the result
self::$init = false;
return true;
}
/**
* Called by spl_autoload when a class needs to be auto loaded
*
* @param string $className Name of the class to auto loadLocalesList (with namespace)
*
* @return bool True on success, false on failure.
*/
// TODO: Should we rename this to load(...); to simplefy the method name?
// TODO: Remove the isClassLoaded() methods, because their results are returned by the previous methods!
public static function loadClass($className) {
// TODO: Log this message using the Logger!
echo '[AutoLoader] Loading class: ' . $className . '<br />';
// TODO: Should we keep this method?
// TODO: If this is an interface, check whether it's loaded!
// TODO: Make sure interfaces won't be loaded each time
if(interface_exists($className))
return true;
// TODO: Is this test required?
// Check whether the class is already loaded
if(self::isClassLoaded($className))
return true;
// Try to loadLocalesList the class if it's registered
self::loadRegisteredClass($className);
// Check whether the class is loaded successfully, if so, return true
if(self::isClassLoaded($className))
return true;
// Try to loadLocalesList the class from a registered namespace
self::loadFromRegisteredNamespace($className);
// Check whether the class is loaded successfully, if so, return true
if(self::isClassLoaded($className))
return true;
// Try to loadLocalesList the file with every registered extension
foreach(self::$regExtensions as $ext) {
// Get the probable file path to the file of the class that needs to be loaded
$classFile = self::getRoot() . DIRECTORY_SEPARATOR . $className . $ext;
// Make sure this file exists
if(!file_exists($classFile))
continue;
// TODO: Fix warning bellow
// TODO: Should we require once?
// Load the class
require_once($classFile);
// Check whether the class is loaded successfully, if so, return true
if(self::isClassLoaded($className))
return true;
}
// Class could not be loaded, show error message
// TODO: Show proper error message
// TODO: Possibility to disable error msg'statements so other class loaders could be called too
// die('Class \'' . $class_name . '\' could not be loaded!');
return false;
}
/**
* Get the root path, the path to load all classes from by default.
*
* @return string Root path
*/
public static function getRoot() {
return self::$root;
}
/**
* Set the root path to load all classes from by default.
*
* @param string|null $root Root path, or null to reset the root path to it's default.
* The Carbon Core path will be used as default root path.
*
* @return bool True if succeed, false if the root path was invalid.
*/
public static function setRoot($root = null) {
// Reset the root directory if the argument equals null
if($root == null)
$root = CARBON_CORE_ROOT;
// Make sure the $root parameter is a string
// TODO: Allow directory and file instances!
if(!is_string($root))
return false;
// TODO: Make sure the root path is valid
// Set the root path, return true
self::$root = $root;
return true;
}
/**
* Check whether a class is loaded
*
* @param string $className Name of the class to check for (with namespace)
*
* @return bool True if this class is loaded, false otherwise
*/
public static function isClassLoaded($className) {
return class_exists($className, false);
}
/**
* Register a new extension to the auto loader
*
* @param string|Array $extensions The extension, or array with extensions to register.
* The parameter extensions must start with period.
* @param bool $prepend True to prepend the extension to the list, instead of appending it (default: false)
*
* @return int Amount of extensions that where successfully added.
* Extensions that where invalid, or that already existed won't count.
*/
// TODO: Make sure this method works!
public static function registerExtension($extensions, $prepend = false) {
// TODO: Show debug messages
// Convert the $extensions param to an array
if(!is_array($extensions))
$extensions = Array($extensions);
// Keep track of the success count
$successCount = 0;
// Add each extension from the array
foreach($extensions as &$ext) {
// Trim the extension from unwanted white spaces and commas
$ext = trim($ext, ", \t\n\r\0\x0B");
// Make sure the extension isn't an empty string
if(strlen($ext) <= 0)
continue;
// Make sure the string doesn't contain any unwanted slashes or spaces
if(StringUtils::contains($ext, array('/', '\\', ' ')))
continue;
// Make sure the extension starts with a period, if not, prepend a period to the extension
if(!StringUtils::startsWith($ext, '.'))
$ext = '.' . $ext;
// Make sure this extension isn't registered already
if(self::isExtensionRegistered($ext))
continue;
// Append or prepend the extension to the list
if(!$prepend)
array_push(self::$regExtensions, $ext);
else
array_unshift(self::$regExtensions, $ext);
// Increase the success count and continue to the next item
$successCount++;
}
// Return the count of successfully registered extensions
return $successCount;
}
/**
* Get a list of registered extensions
*
* @param bool $asArray True to return the list of extensions as an array,
* false to return the list as a string separated with commas (default: true)
* @param bool $stripPeriods True to strip the periods from the extensions (default: false)
*
* @return array|string Array with extensions, or string with extensions separated with commas,
* may return an empty array or string if no extension is registered
*/
public static function getRegisteredExtensions($asArray = true, $stripPeriods = false) {
// TODO: Show debug messages
// Check whether the extensions should be returned as array or as string
if($asArray || $stripPeriods) {
// Get a copy of the array with registered extensions
$extensions = ArrayUtils::copyArray(self::$regExtensions);
// Remove white spaces and commas from all the extension items
foreach($extensions as &$ext)
$ext = trim($ext, ", \t\n\r\0\x0B");
// Strip the periods if required
if($stripPeriods)
foreach($extensions as &$ext)
$ext = ltrim($ext, '.');
// If the list should be returned as a string, implode the array and return the result
if(!$asArray)
return implode(',', $extensions);
// Return the array of extensions
return $extensions;
} else {
// Implode the array, and return the result
return implode(',', self::$regExtensions);
}
}
/**
* Check whether an extension is registered or not
*
* @param string $extension Extension to check for, required prepended period
* @param bool $caseSensitive True to make the extension case sensitive, false otherwise (default: true)
*
* @return bool True if the extension is registered, false otherwise.
* May return false if the extension was invalid.
*/
public static function isExtensionRegistered($extension, $caseSensitive = true) {
// TODO: Show debug messages
// Trim the extension from unwanted white spaces and commas
$extension = trim($extension, ", \t\n\r\0\x0B");
// Make sure the extension isn't an empty string
if(strlen($extension) <= 0)
return false;
// Get a copy of the array with the registered extensions
$extensions = ArrayUtils::copyArray(self::$regExtensions);
// Check whether the current extension entry equals to the argument extension
foreach($extensions as &$ext) {
// Strip all unwanted white spaces and commas from the item
$ext = trim($ext, ", \t\n\r\0\x0B");
// Check whether the argument $extension equals to the $ext item, if so, return true
if(StringUtils::equals($ext, $extension, $caseSensitive, false))
return true;
}
// No match found, return false
return false;
}
/**
* Unregister a registered extension.
*
* @param string|array $extensions Extension or array with extensions to unregister.
* Extensions must start with a period.
*
* @return int Amount of removed/unregistered extensions.
*/
public static function unregisterExtension($extensions) {
// TODO: Show debug messages
// Make sure any extension is registered
if(sizeof(self::$regExtensions))
return 0;
// Make sure any extension was supplied as argument
if(is_array($extensions)) {
if(sizeof($extensions) <= 0)
return 0;
} else
if(strlen(trim($extensions)) <= 0)
return 0;
// If the $extension argument isn't an array already, convert it to an array
if(!is_array($extensions))
$extensions = Array($extensions);
// Remove all unwanted characters from the extensions array, make sure each extension starts with a period
foreach($extensions as &$extension) {
// Trim the extension from unwanted white spaces and commas
$extension = trim($extension, ", \t\n\r\0\x0B");
// Make sure the extension starts with a period
if(!StringUtils::startsWith($extension, '.'))
$extension = '.' . $extension;
}
// Remove all invalid extensions
for($i = 0; $i < sizeof($extensions); $i++) {
// Get the current extension
$ext = $extensions[$i];
// Check whether the current item is blank or not
if(strlen(trim($ext)) <= 0) {
// Remove the current item
unset($extensions[$i]);
// Decrease the index counter, so nothing is skipped
$i--;
}
// Make sure the extension doesn't contain any unwanted slashes or spaces
if(StringUtils::contains($ext, array('/', '\\', ' '))) {
// Remove the current item
unset($extensions[$i]);
// Decrease the index counter, so nothing is skipped
$i--;
}
}
// Keep track of the removed count
$removedCount = 0;
// Loop through each registered extension to check if it should be removed or not
for($i = 0; $i < sizeof(self::$regExtensions); $i++) {
// Loop through each argument extension, to check if it equals to the current extension item
foreach($extensions as &$extension) {
// Check whether the extension entry equals to the current extension
if(StringUtils::equals(self::$regExtensions[$i], $extension, false, false)) {
// Remove the extension
unset(self::$regExtensions[$i]);
// Decrease the index counter so none of the items is skipped,
// increase the removed count and continue to the next item
$i--;
$removedCount++;
break;
}
}
}
// Return the count of removed extensions
return $removedCount;
}
/**
* Register a class with a file path so the proper file will be loaded when the class is being used.
* This will overwrite any classes which where registered before with an identical class name.
*
* @param string $className Name of the class (with namespace)
* @param string $classFile The file to loadLocalesList the class from
*/
public static function registerClass($className, $classFile) {
self::$regClasses[trim($className)] = trim($classFile);
}
/**
* Check whether a class is registered in the Autoloader
*
* @param string $className Name of the class to check for (with namespace)
*
* @return bool True if this class is registered, false otherwise
*/
public static function isClassRegistered($className) {
return array_key_exists(trim($className), self::$regClasses);
}
/**
* Try to loadLocalesList a registered class name
*
* @param string $className Name of the class to loadLocalesList (with namespace)
*
* @return bool True if succeed, false otherwise
*/
public static function loadRegisteredClass($className) {
// TODO: Show debug messages
// Make sure the class name is registered
if(!self::isClassRegistered($className))
return false;
// Get the class file
$classFile = self::$regClasses[trim($className)];
// Make sure the class file exists
if(!file_exists($classFile))
return false;
// Load the class
/** @noinspection PhpIncludeInspection */
include($classFile);
// Succesfully loaded, return true
return true;
}
/**
* Unregister a registered class
*
* @param string $className Name of the class to unregister (with namespace)
*
* @return bool True if any class was unregistered, false otherwise
*/
public static function unregisterClass($className) {
// TODO: Show debug messages
// Make sure any class with this name is registered
if(!self::isClassRegistered($className))
return false;
// Unregister the class
unset(self::$regClasses[trim($className)]);
// Class unregistered, return true
return true;
}
/**
* Register a namespace with a directory path so the proper file will be loaded when a class is being loaded from a
* registered namespace.
* This will overwrite any classes which where registered before with an identical class name.
*
* @param string $namespaceName Name of the namespace
* @param string $namespaceDir The directory to loadLocalesList the classes from
*/
public static function registerNamespace($namespaceName, $namespaceDir) {
// TODO: Show debug messages
// Trim the namespace name from unwanted characters
$namespaceName = rtrim(trim($namespaceName), '\\');
// Trim the namespace directory from unwanted characters
$namespaceDir = rtrim(trim($namespaceDir), "/\\");
// Register the namespace name
self::$regNamespaces[$namespaceName] = $namespaceDir;
}
/**
* Check whether a namespace is registered in the Autoloader
*
* @param string $namespaceName Name of the namespace to check for
*
* @return bool True if this namespace is registered, false otherwise
*/
public static function isNamespaceRegistered($namespaceName) {
// TODO: Show debug messages
// Trim the namespace name from unwanted characters
$namespaceName = rtrim(trim($namespaceName), '\\');
// Check whether this namespace name exists, return the result
return array_key_exists($namespaceName, self::$regNamespaces);
}
/**
* Try to loadLocalesList a class from any registered namespace
*
* @param string $className Name of the class to loadLocalesList (with namespace)
*
* @return bool True if succeed, false otherwise
*/
public static function loadFromRegisteredNamespace($className) {
// TODO: Show debug messages
// Check whether the class is already loaded
if(self::isClassLoaded($className))
return true;
// Trim the class name
$className = trim($className);
// Loop through each registered namespace and check whether this class is inside this namespace
foreach(self::$regNamespaces as $namespace => $namespace_dir) {
// Make sure the class name is longer than the current namespace name
if(strlen($namespace) + 1 > strlen($className))
continue;
// Check whether the class name has the current namespace
// TODO: Run a canonical check!
// TODO: Namespaces aren't case sensitive!
// TODO: Use the ClassUtils class for namespacing and stuff?
if(substr($className, 0, strlen($namespace) + 1) !== $namespace . '\\')
continue;
// Strip the namespace from the class name
$classNameStripped = substr($className, strlen($namespace) + 1);
// Try to loadLocalesList the file with every specified extension
foreach(self::$regExtensions as $ext) {
// Get the probable class file
$classFile = $namespace_dir . DIRECTORY_SEPARATOR . $classNameStripped . $ext;
// Make sure this class file exists
if(!file_exists($classFile))
continue;
// Load the class file
/** @noinspection PhpIncludeInspection */
include($classFile);
// Check whether the class is successfully loaded, if so break the loop
if(self::isClassLoaded($className))
return true;
}
}
// Class couldn't be loaded, return false
return false;
}
/**
* Unregister a registered namespace
*
* @param string $namespaceName Name of the namespace to unregister
*
* @return bool True if any namespace was unregistered, false otherwise
*/
public static function unregisterNamespace($namespaceName) {
// TODO: Show debug messages
// Make sure any namespace with this name is registered
if(!self::isNamespaceRegistered($namespaceName))
return false;
// Unregister the namespace
unset(self::$regNamespaces[trim($namespaceName)]);
// Namespace unregistered, return true
return true;
}
}
<?php
/**
* Cache.php
*
* The CacheHandler class handles all the cache.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
// TODO: Cache cache names to cache keys
namespace carbon\core\cache;
// Prevent direct requests to this file due to security reasons
use carbon\core\io\filesystem\FilesystemObject;
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Handles all the cache
* @package core\language
* @author Tim Visee
*/
class CacheHandler {
/** @var bool True if cache is enabled */
private $cacheEnabled = true;
/** @var FilesystemObject $cacheDir Cache directory */
private $cacheDir;
/** @var string $CACHE_FILE_PREFIX Cache file prefix */
private $CACHE_FILE_PREFIX = '';
/** @var string $CACHE_FILE_SUFFIX Cache file suffix */
private $CACHE_FILE_SUFFIX = '';
/** @var string $CACHE_FILE_EXTENTION Cache file extention */
private $CACHE_FILE_EXTENSION = '.cache';
// TODO: Should use fopen, fwrite, and fclose to gain performance? Maybe get_file_contents is faster due to PHP memory mapping?
/**
* Constructor
*
* @param \carbon\core\io\filesystem\FilesystemObject $cacheDir Cache directory
*/
public function __construct($cacheDir = null) {
// Make sure $cacheDir is set
if(empty($cacheDir))
// Unable to construct Cache class, cache directory not set
// TODO: Show proper error message
die('Error while constructing Cache class!');
// Store the cache directory
$this->cacheDir = $cacheDir->getCanonicalFile();
// TODO: Take a look at those lines bellow
// Generate the .htaccess file (for security reasons)
if(!$cacheDir->isDirectory())
$this->createCacheHtaccessFile($this->cacheDir);
}
/**
* Check whether caching is enabled
*
* @return bool True if caching is enabled, false otherwise
*/
public function isEnabled() {
return $this->cacheEnabled;
}
/**
* Enable or disable cache.
*
* @param bool $cacheEnabled True to enable, false to disable.
*/
public function setEnabled($cacheEnabled) {
$this->cacheEnabled = $cacheEnabled;
}
/**
* Get the cache file of a cached file by it's name
*
* @param string $name Cache name
*
* @return FilesystemObject Cache file
*/
private function getCacheFile($name) {
return new FilesystemObject(
$this->getCacheDirectory(),
$this->CACHE_FILE_PREFIX . $name . $this->CACHE_FILE_SUFFIX . $this->CACHE_FILE_EXTENSION
);
}
/**
* Cache any type of data
*
* @param string $name Cache name
*
* @param mixed $data Data to cache
*/
public function cache($name, $data) {
// Serialize the data
$dataString = serialize($data);
// Get the file to cache the data to
$cacheFile = $this->getCacheFile($name);
// Create the parent directory if it doesn'elapsed exist
$cacheFile->getParentDirectory()->makeDirectory(0777, true);
// Write the cache string to a file
$fh = fopen($cacheFile->getPath(), 'w') or die ('Carbon CMS: Error while writing cache!');
fwrite($fh, $dataString);
fclose($fh);
}
/**
* Cache a string
*
* @param string $name Cache name
*
* @param string $string String to cache
*/
public function cacheString($name, $string) {
// Get the file to cache the data to
$cacheFile = $this->getCacheFile($name);
// Create the parent directory if it doesn'elapsed exist
$cacheFile->getParentDirectory()->makeDirectory(0777, true);
// Write the string to a file
$fh = fopen($cacheFile->getPath(), 'w') or die ('Carbon CMS: Error while writing cache!');
fwrite($fh, $string);
fclose($fh);
}
/**
* Get cached data
*
* @param string $name Cache name
*
* @return mixed Cached data
*/
public function getCache($name) {
// This file has to be available, if not return null
if(!$this->isCached($name))
return null;
// Get the file to cache the data to
$cacheFile = $this->getCacheFile($name);
// Get the cached file contents
$data = file_get_contents($cacheFile->getPath()) or die('Carbon CMS: Error while reading cache!');
// Unserialize the data, return the result
return unserialize($data);
}
/**
* Get a cached string
*
* @param string $name Cache name
*
* @return string Cached string
*/
public function getCachedString($name) {
// This file has to be available, if not return an empty string
if(!$this->isCached($name))
return '';
// Get the file to cache the data to
$cacheFile = $this->getCacheFile($name);
// Get the cached string, return the result
return file_get_contents($cacheFile->getPath()) or die('Error while reading cache!');
}
/**
* Check whether something is cached or not
*
* @param string $name Cache name to check for
*
* @return boolean True if cached, false otherwise
*/
public function isCached($name) {
return $this->getCacheFile($name)->isFile();
}
/**
* Remove cached data
*
* @param string $name Cache name to remove
*/
public function removeCache($name) {
// Get the file to cache the data to
$cacheFile = $this->getCacheFile($name);
// Make sure the file exists, then remove the file
if($cacheFile->exists())
$cacheFile->deleteFile() or die('Carbon CMS: Error while deleting cache!');
}
// TODO: Remove vs delete?
/**
* Remove all cached data
*
* @return int Removed cache files and directories count
*/
public function removeAllCache() {
// Get the cache directory
$cacheDir = $this->getCacheDirectory();
// Make sure the cache directory exists
if(!$cacheDir->exists())
return 0;
// Store the count of removed cache files and directories
$count = $cacheDir->deleteContents(null);
// Recreate the .htaccess file in the cache dire
$this->createCacheHtaccessFile($cacheDir);
// Return amount of removed cache
return $count;
}
/**
* Automatically generate a .htaccess file in the cache folder, due to security reasons.
*
* @param $cacheDir String Cache directory to generate the .htaccess file in.
*/
private function createCacheHtaccessFile($cacheDir) {
// Get the file path to the .htaccess file
$filePath = new FilesystemObject($cacheDir, '.htaccess');
// Generate the file contents
$fileContents = "# This set_file has automatically been generated by Carbon CMS." . PHP_EOL;
$fileContents .= "#" . PHP_EOL;
$fileContents .= "# This .htaccess file is used to protect the cache folder from any security issues such as hackers." . PHP_EOL;
$fileContents .= "# Do not remove this file!" . PHP_EOL;
$fileContents .= PHP_EOL;
$fileContents .= "Deny From All";
// Write the .htaccess file
$fileHandler = fopen($filePath->getCanonicalPath(), 'w') or die ('Carbon CMS: Error while generating .htaccess file in the cache directory!');
fwrite($fileHandler, $fileContents);
fclose($fileHandler);
}
/**
* Get the age of a cache file in seconds.
*
* @param string $name Cache name to get the age from.
*
* @return int Age in seconds, a negative number if an error occurred.
*/
public function getCacheAge($name) {
// This file has to be available, if not return negative 1
if(!$this->isCached($name))
return -1;
// Get the cache file
$cacheFile = $this->getCacheFile($name);
// Get the modification timestamp of the file
$fileModificationTime = $cacheFile->getModificationTime();
// Make sure the modification time is a number
if(!is_int($fileModificationTime))
return -1;
// Return the time difference
return time() - $fileModificationTime;
}
/**
* Get the location of the cache directory.
*
* @return \carbon\core\io\filesystem\FilesystemObject Cache directory location.
*/
public function getCacheDirectory() {
return $this->cacheDir;
}
/**
* Set the location of the cache directory
*
* @param string $cacheDir Cache directory location
*/
public function setCacheDirectory($cacheDir) {
$this->cacheDir = rtrim($cacheDir, '/') . '/';;
}
/**
* Get the cache file prefix
*
* @return string Cache file prefix
*/
public function getCachePrefix() {
return $this->CACHE_FILE_PREFIX;
}
/**
* Set the cache file prefix
*
* @param string $prefix Cache file prefix
*
* @return bool True if succeed, false on failure.
*/
public function setCachePrefix($prefix) {
// The cache prefix must be a string
if(!is_string($prefix))
return false;
// Change the suffix
$this->CACHE_FILE_PREFIX = $prefix;
return true;
}
/**
* Get the cache file suffix
* @return string Cache file suffix
*/
public function getCacheSuffix() {
return $this->CACHE_FILE_SUFFIX;
}
/**
* Set the cache file suffix
*
* @param string $suffix Cache file suffix
*
* @return bool True if succeed, false on failure.
*/
public function setCacheSuffix($suffix) {
// The cache suffix must be a string
if(!is_string($suffix))
return false;
// Change the suffix
$this->CACHE_FILE_SUFFIX = $suffix;
return true;
}
/**
* Get the cache file extension
*
* @return string Cache file extension
*/
public function getCacheExtension() {
return $this->CACHE_FILE_EXTENSION;
}
/**
* Set the cache file extension
*
* @param string $extension Cache extension
*
* @return bool True if succeed, false on failure.
*/
public function setCacheExtension($extension) {
// The cache extension must be a string
if(!is_string($extension))
return false;
// Change the extension
$this->CACHE_FILE_EXTENSION = $extension;
return true;
}
}
<?php
namespace carbon\core\cache\simplecache;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
// TODO: Better class naming!
/**
* A high speed, simple caching class which allows variables to be cached temporarily.
* This class provides an easy to use interface to manage cached values.
*
* @package carbon\core\cache\simplecache
*/
class SimpleCache {
/** @var array Array holding all cached values. */
private $cache = Array();
/**
* Constructor
*/
public function __construct() { }
/**
* Get a cached value.
*
* @param mixed $key Valid cache key.
* @param mixed $default [optional] Default value returned on failure or if the cached value didn't exist.
*
* @return mixed Cached value. The default value will be returned on failure or if the cached value didnt' exist.
*/
public function get($key, $default = null) {
// Make sure this cache entry exists
if(!$this->has($key))
return $default;
// Return the cache value
return @$this->cache[$key];
}
/**
* Cache a value. This overwrites a cached value with the same key.
*
* @param mixed $key Valid cache key.
* @param mixed $value Cache value
*
* @return bool True on success, false on failure due to an invalid cache key.
*/
public function set($key, $value) {
// Make sure the cache key is valid
if(!$this->isValidKey($key))
return false;
// Set the cache value and return true
$this->cache[$key] = $value;
return true;
}
/**
* Check whether a value is cached based on it's cache key.
*
* @param mixed $key Valid cache key.
*
* @return bool True if the cached value exists, false otherwise.
*/
public function has($key) {
// Make sure the cache key is valid
if(!$this->isValidKey($key))
return false;
// Check whether the cache key exists, return the result
return isset($this->cache[$key]);
}
/**
* Delete or unset a cached value.
*
* @param mixed $key Valid cache key.
*
* @return bool True if any cached value was deleted, false otherwise.
*/
public function delete($key) {
// Make sure this cache entry exists
if(!$this->has($key))
return false;
// Delete the cache value, return the result
unset($this->cache[$key]);
return true;
}
/**
* Get the number of cached values.
*
* @return int Number of cached values.
*/
public function getSize() {
return sizeof($this->cache);
}
/**
* Flush the cache
*
* @return int Number of flushed cache values, a negative number on failure.
*/
public function flush() {
// Get the number of cached values
$count = $this->getSize();
// Flush the cache and return the number of flushed values
$this->cache = Array();
return $count;
}
/**
* Check whether a cache key is valid or not.
*
* @param mixed $key Cache key to check.
*
* @return bool True if the cache key is valid, false otherwise.
*/
public function isValidKey($key) {
return !empty($key);
}
}
<?php
// TODO: Add ability to read PHP or INI files (support for both!)
/**
* Config.php
*
* The ConfigHandler class handles the configuration file of Carbon CMS.
*
* Reads the Carbon CMS configuration file.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\config;
use carbon\core\exception\config\CarbonConfigException;
use carbon\core\exception\config\CarbonConfigLoadException;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\util\ArrayUtils;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Handles the configuration file of Carbon CMS.
*
* @package core
* @author Tim Visee
*/
class ConfigHandler {
// TODO: Improve the 'required keys' feature bellow!
/** @var $configRequiredKeys Array Array of required keys in a configuration array */
private static $configRequiredKeys = null;
/** @var FilesystemObject $cfg_path File path of the configuration file */
private $configFile = null;
/** @var Array $cfg_arr Array containing the values when the file has been loaded */
private $configArray = null;
/**
* Constructor.
*
* @param \carbon\core\io\filesystem\FilesystemObject $configFile The configuration file, null to use the default config file.
* @param bool $load True to automatically load the configuration file, false otherwise.
*/
public function __construct($configFile = null, $load = true) {
// Set some defaults
self::$configRequiredKeys = Array(
"general" => Array(
"site_url",
"site_path"
),
"database" => Array(
"host",
"port",
"database",
"username",
"password",
"table_prefix"
),
"hash" => Array(
"hash_algorithm",
"hash_key"
),
"carbon" => Array(
"debug"
)
);
// Make sure the configuration file path is set and make sure it's an instance of File, if not, use the default
// configuration file path
if(empty($configFile) || !($configFile instanceof FilesystemObject))
$configFile = new FilesystemObject(CARBON_CMS_CONFIG);
$this->configFile = $configFile;
// Load the configuration file if $load equals to true
if($load)
$this->load();
}
/**
* Load the configuration file
*/
public function load() {
// Make sure the configuration file exists
if(!$this->configFile->exists())
// The configuration file doesn'elapsed exist, throw an exception
throw new CarbonConfigLoadException(
'Unable to loadLocalesList configuration set_file from \'' . $this->configFile->getPath() . '\', set_file does not exist!',
0, null,
'Create a configuration set_file: \'' . $this->configFile->getPath() . '\'');
// Load the configuration file
$configArray = include($this->configFile->getPath());
// Make sure loading the configuration file succeed
if($configArray === false || !is_array($configArray))
// Error occured while loading the configuration file, throw an exception
throw new CarbonConfigLoadException(
'An error occured while parsing the configuration set_file!',
0, null,
'Make sure the data of the configuration set_file is valid.');
// Make sure the configuration array is valid
$this->isValidConfigArray($configArray, true);
// Store a copy of the configuration values
$this->configArray = ArrayUtils::copyArray($configArray);
}
/**
* Flush the loaded configuration
*/
public function flushConfig() {
// Reset the configuration values array to unload the config file
$this->configArray = array();
}
/**
* Check if a configuration file is loaded
*
* @return bool True if loaded
*/
public function isConfigLoaded() {
// Make sure the configuration data is an array
if(!is_array($this->configArray))
return false;
// Make sure the array contains any items
return (sizeof($this->configArray) > 0);
}
/**
* Get akk keys from a configuration array section
*
* @param string $section The section to get the keys from
* @param array|null $configArray The configuration array to get the keys from,
* null to use the current loaded configuration array
*
* @return array|null Array with keys from the section, or null if the section or configuration array was invalid.
*/
public function getKeys($section, $configArray = null) {
// Use the default configuration array if the param equals to null
if($configArray == null)
$configArray = $this->configArray;
// The configuration array may not be null and must be an array
if($configArray == null || !is_array($configArray))
return false;
// Make sure this section exists
if(!$this->hasSection($section, $configArray))
return null;
// Return the config keys
return $configArray[$section];
}
/**
* Get a value from a loaded configuration file
*
* @param string $section The section in the configuration file
* @param string $key The key in the configuration file
* @param mixed $default The default value returned if the key was not found
* @param array|null $configArray The configuration array to get the value from,
* null to use the current loaded configuration array
*
* @return bool The value from the configuration file, or the default value if the key was not found
*/
public function get($section, $key, $default = null, $configArray = null) {
return $this->getValue($section, $key, $default, $configArray);
}
/**
* Get a value from a loaded configuration file
*
* @param string $section The section in the configuration file
* @param string $key The key in the configuration file
* @param mixed $default The default value returned if the key was not found
* @param array $configArray The configuration array to get the value from,
* null to use the current loaded configuration array
*
* @return bool The value from the configuration file, or the default value if the key was not found
*/
public function getValue($section, $key, $default = null, $configArray = null) {
// Use the default configuration array if the param equals to null
if($configArray === null)
$configArray = $this->configArray;
// The configuration array may not be null and must be an array
if($configArray === null || !is_array($configArray))
return false;
// Make sure the section and the key are booth available
if(!$this->hasSection($section, $configArray) || !$this->hasKey($section, $key, $configArray))
return $default;
// Return the value from the config
return $configArray[$section][$key];
}
/**
* Get a boolean from a loaded configuration file
*
* @param string $section The section in the configuration file
* @param string $key The key in the configuration file
* @param bool $default The default value returned if the key was not found
* @param array $configArray The configuration array to get the value from,
* null to use the current loaded configuration array
*
* @return bool The boolean from the configuration file, or the default boolean value
*/
public function getBool($section, $key, $default = false, $configArray = null) {
return (bool) $this->getValue($section, $key, $default, $configArray);
}
/**
* Check if a configuration array section exists
*
* @param string $section The section to search for
* @param array $configArray The configuration array to search in,
* null to use the current loaded configuration array
*
* @return bool True if the section was found
*/
public function hasSection($section, $configArray = null) {
// Use the default configuration array if the param equals to null
if($configArray == null)
$configArray = $this->configArray;
// The configuration array may not be null and must be an array
if($configArray == null || !is_array($configArray))
return false;
// Check if the config array contains this section
return array_key_exists($section, $configArray);
}
/**
* Check if the configuration file has a specified key
*
* @param string $section The section to search in
* @param string $key The key to search for
* @param array $configArray The configuration array to check in,
* null to use the current loaded configuration file array
*
* @return bool True if the configuration file has this key
*/
public function hasKey($section, $key, $configArray = null) {
// Use the default configuration array if the param equals to null
if($configArray == null)
$configArray = $this->configArray;
// The configuration array may not be null and must be an array
if($configArray == null || !is_array($configArray))
return false;
// Check if the config array contains this section
if(!$this->hasSection($section, $configArray))
return false;
// Check if this config array contains this key
return array_key_exists($key, $configArray[$section]);
}
/**
* Check whether a configuration array contains all the required items
*
* @param string $configArray The configuration array to check in
* @param bool $throwException
*
* @throws CarbonConfigException Throws exception when the configuration array is invalid and $throw_exception equals to true
* @return bool True if the configuration array was valid, false otherwise
*/
public function isValidConfigArray($configArray, $throwException = false) {
// Make sure the param is an array
if(!is_array($configArray))
return false;
// Make sure the array contains any items
if(sizeof($configArray) <= 0)
return false;
// Loop through each required key and section and make sure it exists
foreach(self::$configRequiredKeys as $section => $keys) {
// Make sure the current section exists
if(!$this->hasSection($section, $configArray)) {
// If set, throw an exception
if($throwException)
throw new CarbonConfigException(
'Missing section in the configuration set_file: \'' . $section . '\'',
0, null,
'Add the \'' . $section . '\' section to the configuration set_file.');
return false;
}
// Make sure all the keys exist inside the current section
foreach(self::$configRequiredKeys[$section] as $key) {
if(!$this->hasKey($section, $key, $configArray)) {
// If set, throw an exception
if($throwException)
throw new CarbonConfigException(
'Missing key in the configuration set_file: \'' . $section . '.' . $key . '\'',
0, null,
'Add the \'' . $section . '.' . $key . '\' key to the configuration set_file.');
return false;
}
}
}
// Configuration set_file is valid, return true
return true;
}
}
<?php
// TODO: Extend this class
// TODO: Update PHP Docs in this class
/**
* Core.php
*
* The Core class supplies all the core class instances like the database and the config handler.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\config\ConfigHandler;
use carbon\core\log\Logger;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Supplies all the core class instances.
* @package carbon\core
* @author Tim Visee
*/
class Core {
/** @var Logger $log Logger instance */
private static $log = null;
/** @var ConfigHandler $cfg ConfigHandler holder */
private static $cfg = null;
/** @var Router $router Router holder */
private static $router = null;
/** @var Database $db Database holder */
private static $db = null;
/** @var RegistryHandler $options RegistryHandler holder */
private static $registry = null;
/** @var CacheHandler $cache CacheHandler holder */
private static $cache = null;
/** @var UserManager $user_man UserManager holder */
private static $user_man = null;
/** @var EventManager $event_man EventManager holder */
private static $event_man = null;
/** @var PluginManager $plugin_man PluginManager holder */
private static $plugin_man = null;
/** @var PageManager $page_man PageManager holder */
private static $page_man = null;
/**
* Get the logger instance.
*
* @return Logger Logger instance.
*/
public static function getLogger() {
return self::$log;
}
/**
* Set the logger instance
*
* @param Logger $log Logger instance
*/
public static function setLogger($log) {
self::$log = $log;
}
/**
* Get the ConfigHandler instance
* @return ConfigHandler ConfigHandler instance
*/
public static function getConfig() {
return self::$cfg;
}
/**
* Set the ConfigHandler instance
* @param ConfigHandler $cfg ConfigHandler instance
*/
public static function setConfig(ConfigHandler $cfg) {
self::$cfg = $cfg;
}
/**
* Get the Router instance
* @return Router Router instance
*/
public static function getRouter() {
return self::$router;
}
/**
* Set the Router instance
* @param Router $router Router instance
*/
public static function setRouter(Router $router) {
self::$router = $router;
}
/**
* Get the Database instance
* @return Database Database instance
*/
public static function getDatabase() {
return self::$db;
}
/**
* Set the Database instance
* @param Database $db Database instance
*/
public static function setDatabase(Database $db) {
self::$db = $db;
}
/**
* Get the RegistryHandler instance
* @return RegistryHandler RegistryHandler instance
*/
public static function getRegistry() {
return self::$registry;
}
/**
* Set the RegistryHandler instance
* @param RegistryHandler $options RegistryHandler instance
*/
public static function setRegistry(RegistryHandler $options) {
self::$registry = $options;
}
/**
* Get the CacheHandler instance
* @return CacheHandler instance
*/
public static function getCache() {
return self::$cache;
}
/**
* Set the CacheHandler instance
* @param CacheHandler $cache CacheHandler instance
*/
public static function setCache(CacheHandler $cache) {
self::$cache = $cache;
}
/**
* Get the UserManager instance
* @return UserManager UserManager instance
*/
public static function getUserManager() {
return self::$user_man;
}
/**
* Set the UserManager instance
* @param UserManager $user_man UserManager instance
*/
public static function setUserManager(UserManager $user_man) {
self::$user_man = $user_man;
}
/**
* Get the EventManager instance
* @return EventManager EventManager instance
*/
public static function getEventManager() {
return self::$event_man;
}
/**
* Set the EventManager instance
* @param EventManager $event_man EventManager instance
*/
public static function setEventManager(EventManager $event_man) {
self::$event_man = $event_man;
}
/**
* Get the PluginManager instance
* @return PluginManager PluginManager instance
*/
public static function getPluginManager() {
return self::$plugin_man;
}
/**
* Set the PluginManager instnace
* @param PluginManager $plugin_man PluginManager instance
*/
public static function setPluginManager(PluginManager $plugin_man) {
self::$plugin_man = $plugin_man;
}
/**
* Get the PageManager instance
* @return PageManager PageManager instance
*/
public static function getPageManager() {
return self::$page_man;
}
/**
* Set the PageManager instance
* @param PageManager $page_man PageManager instance
*/
public static function setPageManager(PageManager $page_man) {
self::$page_man = $page_man;
}
/**
* Get the version code of this installed Carbon CMS instance.
* The version code is a number which is incremented by one each release.
* @return int Carbon CMS version code
*/
public static function getVersionCode() {
return CARBON_CMS_VERSION_CODE;
}
/**
* Get the version name of this installed Carbon CMS instance.
* The version name is a version identifier separated by dots.
* @return string Carbon CMS version name
*/
public static function getVersionName() {
return CARBON_CMS_VERSION_NAME;
}
}
<?php
// TODO: Add debug modes to this class for easy built-in debugging?
namespace carbon\core\database;
// Prevent direct requests to this file due to security reasons
use carbon\core\util\ClassUtils;
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Database class.
*
* @package carbon\core\database
*/
abstract class Database {
/** @var DatabaseConnector|null $con Database connector */
protected $con = null;
/**
* Get the database connector
* @return DatabaseConnector|null Database connector instance or null.
*/
public function getConnector() {
return $this->con;
}
/**
* Set the database connector.
*
* @param DatabaseConnector|null $con Database connector instance or null.
*
* @return bool True if the database connector was set successfully. False otherwise.
*/
protected function setConnector($con = null) {
// Make sure the $con param is an instance of a database connector or null.
if($con !== null)
if(!$con instanceof DatabaseConnector)
return false;
// Set the database connector, return true
$this->con = $con;
return true;
}
/**
* Check whether there'statements an active connection to the server.
* @return bool True if there's an active connection to the database, false otherwise
*/
public function isConnected() {
// Make sure a connector is set
if($this->con === null)
return false;
if(!$this->con instanceof DatabaseConnector)
return null;
// Check whether there's an active connection, return the result
return $this->con->isConnected();
}
/**
* Start a transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionStart() {
return $this->con->transactionStart();
}
/**
* Commit the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionCommit() {
return $this->con->transactionCommit();
}
/**
* Rollback the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionRollBack() {
return $this->con->transactionRollBack();
}
/**
* Check whether we're inside a transaction.
*
* @return bool True if we're inside a transaction, false otherwise.
*/
public function isInTransaction() {
return $this->con->isInTransaction();
}
/**
* Check whether this connector has transaction support.
*
* @return bool True if this connector has transaction support, false otherwise
*/
public function isTransactionSupported() {
return $this->con->isTransactionSupported();
}
/**
* Execute a single, or multiple queries and return the number of affected rows.
*
* @param string|DatabaseStatement|DatabaseBatch|Array $queries
* Database query as a string, as a DatabaseStatement or a few queries as a DatabaseBatch.
* Multiple of the above could also be combined in an array, which may contain even more sub-arrays.
*
* @return int Number of total affected rows
*/
// TODO: Check whether queries are only supported (and not the other statement types)
public function exec($queries) {
// Convert the queries into a query list
$queries = self::asStatementList($queries);
// Make sure the $queries variable isn't null
if($queries === null)
return 0;
// Create a variable to keep track of the number of affected rows
$rows = 0;
// Execute each query
foreach($queries as $query) {
// TODO: Execute query, add number of affected rows to $rows
}
// Return the number of affected rows
return $rows;
}
/**
* Create a list of statement strings based on a single statement string, a DatabaseStatement, a DatabaseBatch or an array
* containing any of these items. The array may contain sub-arrays with items.
*
* @param string|DatabaseStatement|DatabaseBatch|Array $statements A database statement string, a DatabaseStatement,
* a DatabaseBatch or an array with any of these items. The array may also contain sub-arrays with items.
*
* @return Array|null Array containing all statements as a string. Null on failure.
*/
// TODO: Return as database statement instead of a regular string
protected static function asStatementList($statements) {
// Get the DatabaseStatement class dynamically
// TODO: Use the DatabaseDriver class to dynamically get the proper statement class
$dbStatementClass = ClassUtils::getNamespace(get_called_class()) . '\\DatabaseStatement';
// Make sure the $statements param isn't null
if($statements === null)
return null;
// Process the $statements param is it's a string
if(is_string($statements))
return Array(new $dbStatementClass($statements));
// Process the $statement param if it's a DatabaseStatement
if($statements instanceof DatabaseStatement)
// TODO: Make sure the statement is valid
return Array($statements);
// Process the $statement param if it's a DatabaseBatch
if($statements instanceof DatabaseBatch)
return self::asStatementList($statements->getStatements());
// Make sure the $statements param is an array
if(!is_array($statements))
return null;
// Create an array to hold all statements which need to be returned
$out = Array();
// Add each statement from the array to the return list
foreach($statements as $statement) {
// Convert the entry into a statement list
$list = self::asStatementList($statement);
// Make sure the result isn't null
if($list === null)
return null;
// Add the result to the statement list
$out = array_merge($out, $list);
}
// Return the list of statements
return $out;
}
protected $preparedStatement = null;
/**
* Execute an SQL statement. The result set will be returned as a DatabaseStatement object.
*
* @param string $query Database query or statement to execute
*
* @return DatabaseStatement Database statement
*/
public abstract function query($query);
public function prepare($statement) {
// Make sure $statement is valid
if(!DatabaseStatement::isValid($statement, false))
return false;
// TODO: Unprepare current prepared statement?
// TODO: Should the statement be prepared in the current database connector?
// Prepare the statement
$this->preparedStatement = $statement;
}
/**
* Quote a database value
*
* @param $value
*
* @return string
*/
public function quote($value) {
// Quote the value
// TODO: Increase quality of quotes. Make sure numbers aren't quoted
return '`' . $value . '`';
}
// TODO: Methods to implement, based on PDO
/*
* public abstract function errorCode();
public abstract function errorInfo();
public abstract function getAttribute(int $attribute);
public abstract function setAttribute(int $attribute, mixed $value);
public abstract function getDriverName();
public abstract static function getAvailableDrivers();
*/
}
<?php
namespace carbon\core\database;
class DatabaseBatch {
// TODO: Class not finished and/or tested yet!
/** @var array $statements Array of all database statements in this batch */
private $statements = Array();
/**
* Add new statements to the batch
*
* @param DatabaseStatement|array $statements Database statement to add to the batch, or array of database statements to add
*
* @return int Amount of database statements added.
*/
public function addStatement($statements) {
if(!is_array($statements))
// Keep track of the amount of added statements
$count = 0;
// Add each statement
foreach($statements as &$entry) {
// Make sure the statement is valid. The statement may not be null and must be an instance of
// DatabaseStatement
if($entry === null)
continue;
if(!($entry instanceof DatabaseStatement))
continue;
// Add the statement to the batch
array_push($this->statements, $entry);
$count++;
}
// Return the amount of added statements
return $count;
}
/**
* Get a database statement from this batch based on an index value
*
* @param int $statementIndex Index of the database statement to get
*
* @return DatabaseStatement|null Database statement corresponding to the supplied index, or null if the index was out of bound or invalid.
*/
public function getStatement($statementIndex) {
// Make sure the index value is an integer, if not, return null
if(!is_int($statementIndex))
return null;
// Make sure the index is in proper bound, if not, return null
if($statementIndex < 0 || $statementIndex >= $this->getStatementsCount())
return null;
// Get and return the proper statement based on the index
return $this->statements[$statementIndex];
}
/**
* Get all database statements in the current batch.
*
* @return array Array of database statements in the current batch. An empty array will be returned if the batch
* doesn'elapsed have any statements yet.
*/
public function getStatements() {
return $this->statements;
}
/**
* Remove all statements from this batch.
*
* @return int Amount of removed statements
*/
public function removeAllStatements() {
// Keep track of the statements count
$count = $this->getStatementsCount();
// Clear the list of statements in this batch
$this->statements = Array();
// Return the amount of removed statements
return $count;
}
/**
* Get the amount of statements in this batch
*
* @return int Amount of statements in this batch
*/
public function getStatementsCount() {
return sizeof($this->statements);
}
// TODO: Method to execute all statements at once, possibly with rollback
}
<?php
// TODO: Class not finished and/or tested yet!
namespace carbon\core\database;
// Prevent direct requests to this file due to security reasons
use carbon\core\database\driver\mysql\DatabaseStatement;
defined('CARBON_CORE_INIT') or die('Access denied!');
class DatabaseConnectionManager {
// TODO: Decide whether this manager manages DatabaseConnector or Database instances!
// TODO: Automatically add connection instances to the manager when connections are made!
/** @var array Array holding all active database connection instances. */
private static $cons = Array();
/**
* Initialize
*/
public static function init() {
// TODO: Initialize...
}
/**
* Add a new connection to the connection manager
*
* @param $con DatabaseConnector instance to add
*
* @return bool True if the connection was successfully added, false otherwise.
* False will be returned if the connection instance was invalid, or if the connection was managed already.
*/
public static function add($con) {
// Make sure the connection instance isn't null and that the connection is an instance of DatabaseConnector
if($con === null)
return false;
if(!($con instanceof DatabaseStatement))
return false;
// Make sure this connection instance isn't added already
if(self::isManaged($con))
return false;
// Add the connection instance,= to the list and return true
array_push(self::$cons, $con);
return true;
}
/**
* Remove a connection from the connection manager
*
* @param DatabaseConnector $con Connection instance to remove
*
* @return int Count of removed connections. -1 will be returned if the connection instance was invalid.
*/
public static function remove($con) {
// Make sure the connection instance isn't null and that the connection is an instance of DatabaseConnector
if($con === null)
return -1;
if(!($con instanceof DatabaseStatement))
return -1;
// Keep track of the count of removed instances
$count = 0;
// Remove the param instances from the connections list
foreach(self::$cons as $key => &$val) {
if($con == $val) {
unset(self::$cons[$key]);
$count++;
}
}
return $count;
}
/**
* Check whether a connection is being managed
*
* @param $con
*
* @return bool
*/
public static function isManaged($con) {
return in_array($con, self::$cons, true);
}
/**
* Get a list of active database connections.
*
* @return array List of active database connection
*/
public static function getConnections() {
// TODO: Return a list of active database connections?
return self::$cons;
}
/**
* Get the count of active database connections.
*
* @return int Count of active database connections
*/
public static function getConnectionCount() {
return sizeof(self::$cons);
}
}
<?php
/**
* DatabaseConnector.php
* The database connector class which is an abstract base for all database connector classes.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\database;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* DatabaseConnector class
*
* @package core\database
* @author Tim Visee
*/
abstract class DatabaseConnector {
// TODO: Support charsets?
/** string Default database host */
const DEF_HOST = '127.0.0.1';
/** int|null Default database port */
const DEF_PORT = 3306;
/** string|null Default database username */
const DEF_USER = null;
/** string|null Default database password */
const DEF_PASS = null;
/** string Default database name */
const DEF_DBNAME = '';
/** @var string Database host */
protected $host = self::DEF_HOST;
/** @var int|null Database port */
protected $port = self::DEF_PORT;
/** @var string|null Database user */
protected $user = self::DEF_USER;
/** @var string|null Database password */
protected $pass = self::DEF_PASS;
/** @var string Database name to connect to */
protected $dbname = self::DEF_DBNAME;
/**
* Constructor
*
* @param string $host Database host
* @param int|null $port Database port
* @param string|null $user Database username
* @param string|null $pass Database password
* @param string $dbname Database name
* @param bool $connect True to automatically connect
*/
public abstract function __construct($host = self::DEF_HOST, $port = self::DEF_PORT, $user = self::DEF_USER, $pass = self::DEF_PASS, $dbname = self::DEF_DBNAME, $connect = true);
public function __destruct() {
// TODO: Close the database connection nicely, if it's still active!
}
/**
* Connect to the database
*
* @return bool True if succeed, false otherwise
*/
public abstract function connect();
/**
* Disconnect from the database
*/
public abstract function disconnect();
/**
* Reconnect to the database
*
* @return bool True if succeed
*/
public function reconnect() {
// Disconnect from the database
$this->disconnect();
// Reconnect to the database, return the result
return $this->connect();
}
/**
* Check whether there'statements an active connection
*
* @return bool True if connected, false otherwise
*/
public abstract function isConnected();
/**
* Get the database host
*
* @return string Database host
*/
public function getHost() {
return $this->host;
}
/**
* Set the database host. Has to reconnect to the database to use a new host.
*
* @param string $host Database host
*
* @return bool True if the host has been changed, false if the host was invalid
*/
protected function setHost($host = self::DEF_HOST) {
// Make sure the host is a string
if(!is_string($host))
return false;
// Set the host
$this->host = $host;
return true;
}
/**
* Get the database port. Null will be returned if no port is specified.
*
* @return int|null Database port
*/
public function getPort() {
return $this->port;
}
/**
* Set the database port. Has to reconnect to the database to use a new port.
* @param int|null $port Database port
* @return bool True if the port number has been changed, false if the port number is invalid
*/
protected function setPort($port = self::DEF_PORT) {
// Make sure the port is a valid number
if(!is_int($port) && $port !== null)
return false;
// Make sure the port is in valid range
if(($port < 1 || $port > 65535) && $port !== null)
return false;
// Set the port number
$this->port = $port;
return true;
}
/**
* Get the database user. Null will be returned if no user is specified.
*
* @return string|null Database user
*/
public function getUser() {
return $this->user;
}
/**
* Set the database user. Has to reconnect to the database to use the new user.
*
* @param string|null $user Database user. May be null to connect anonymously.
*
* @return bool True if the user has been changed, false if the user was invalid
*/
protected function setUser($user = self::DEF_USER) {
// Make sure the user is a string
if(!is_string($user) && $user !== null)
return false;
// Set the database user
$this->user = $user;
return true;
}
/**
* Get the database password. Null will be returned if no password is specified.
*
* @return string|null Database password
*/
public function getPass() {
return $this->pass;
}
/**
* Set the database password. Has to reconnect to the database to use the new pass.
*
* @param string|null $pass Database password. May be null to connect anonymously.
*
* @return bool True if the pass has been changed, false if the pass was invalid
*/
protected function setPass($pass = self::DEF_PASS) {
// Make sure the pass is a string
if(!is_string($pass) && $pass !== null)
return false;
// Set the database pass
$this->pass = $pass;
return true;
}
/**
* Get the database name
*
* @return string Database name being used
*/
public function getDatabaseName() {
return $this->dbname;
}
/**
* Set the database name. Has to reconnect to the database to use the new database name.
*
* @param string $dbname Database name to use.
*
* @return bool True if the database name has been changed, false if the database name was invalid
*/
protected function setDatabaseName($dbname = self::DEF_DBNAME) {
// Make sure the database name is a string
if(!is_string($dbname))
return false;
// Set the database name
$this->dbname = $dbname;
return true;
}
// TODO: Should we add a exec() method like PDO::exec() ?
// TODO: Should we implement attributes like PDO::getAttribute() ?
/**
* Executes a database statement. The result will be returned as a DatabaseStatement.
*
* @param DatabaseStatement $statement Database statement to execute
*
* @return DatabaseStatement Returned result set as a DatabaseStatement
*/
public abstract function query($statement);
/**
* Start a transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public abstract function transactionStart();
/**
* Commit the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public abstract function transactionCommit();
/**
* Rollback the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public abstract function transactionRollBack();
/**
* Check whether we're inside a transaction.
*
* @return bool True if we're inside a transaction, false otherwise.
*/
public abstract function isInTransaction();
/**
* Check whether this connector has transaction support.
*
* @return bool True if this connector has transaction support, false otherwise
*/
public abstract function isTransactionSupported();
}
<?php
/**
* Created by PhpStorm.
* User: Tim
* Date: 5-8-14
* Time: 15:21
*/
namespace carbon\core\database;
use carbon\core\io\filesystem\FilesystemObject;
class DatabaseDriver {
private $data = Array();
public function __constructor($id = null, $name = null, $namespace = null, $version_code = null, $version_name = null) {
}
public static function loadFromDirectory($dir) {
// Make sure the $dir param isn't null
if($dir === null)
return null;
// Convert the $dir param into a File instance if possible, return null otherwise
if(!$dir instanceof FilesystemObject) {
if(!is_string($dir))
return null;
$dir = new FilesystemObject($dir);
}
// Get the driver file, and make sure this file exists
$settingsFile = new FilesystemObject($dir, 'driver.ini');
if(!$settingsFile->isFile())
return null;
// Read the settings file
$settingsArr = parse_ini_file($settingsFile->getPath(), true);
echo '<pre>';
print_r($settingsArr);
echo '</pre>';
die();
}
}
<?php
namespace carbon\core\database;
use carbon\core\io\filesystem\directory\Directory;
use carbon\core\io\filesystem\directory\DirectoryScanner;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseDriverManager {
/** @var \carbon\core\io\filesystem\directory\Directory Defines the root directory of all database drivers. */
private $driverRoot;
/**
* Constructor
*
* @param \carbon\core\io\filesystem\directory\Directory|null $driverRoot [optional] Driver root directory as a directory instance or
* the path of the root directory as a string. The default root directory will be used if no directory was supplied.
*/
public function __construct($driverRoot = null) {
// Set the driver root
$this->setDriverRoot($driverRoot);
// Refresh the available drivers
$this->refresh();
}
/**
* Refresh the list of available database drivers on the system.
*/
public function refresh() {
// TODO: Implement some form of caching!
// Automatically register namespaces for drivers with different namespace structures
// Make sure the driver directory is valid, if not return null
if(!$this->driverRoot->isDirectory())
return null;
// Construct a directory scanner which will be used to list all available drivers
$ds = new DirectoryScanner($this->driverRoot);
$dirs = $ds->readAll(, null);
// TODO: Check each directory for valid drivers!
// TODO: Store the result
// TODO: Return some status!
}
/**
* Get the driver root directory.
*
* @return Directory Driver root directory.
*/
public function getDriverRoot() {
return $this->driverRoot;
}
/**
* Set the driver root directory.
*
* @param null $driverRoot [optional] Driver root directory path.
*/
public function setDriverRoot($driverRoot = null) {
// Check whether the default driver root should be used
if($driverRoot === null || !is_string($driverRoot))
$driverRoot = __DIR__ . "/driver";
// Set the driver root
$this->driverRoot = $driverRoot;
}
/**
* Get the list of available drivers. The driver list will be refreshed automatically as it's needed.
*
* @return Array|null A list of available database drivers, or null on failure.
*/
public function getDrivers() {
// TODO: Return a list of available drives
}
/**
* Get the default root directory for database drivers.
*
* @return Directory The default root directory for database drivers.
*/
public static function getDefaultDriverRootDirectory() {
return new Directory(__DIR__ . '/driver');
}
}
<?php
namespace carbon\core\database;
class DatabaseFactory {
public static function getInstance() {
// TODO: Return a new database instance!
}
}
<?php
namespace carbon\core\database;
class DatabaseManager {
private $dbs = Array();
public function __construct() { }
public function getDatabases() {
return $this-dbs;
}
}
<?php
namespace carbon\core\database;
use carbon\core\util\StringUtils;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseStatement {
// TODO: Choose proper characters for the constants bellow!
/** string Prefix for dynamic fields */
const DYNAMIC_PREFIX = '{{';
/** string Suffix for dynamic fields */
const DYNAMIC_SUFFIX = '}}';
/** string Dynamic table identifier */
const DYNAMIC_TABLE_IDENTIFIER = '#';
/** string Dynamic parameter identifier */
const DYNAMIC_PARAM_IDENTIFIER = ':';
/** @var array $params Array containing all bound parameters */
private $params = Array();
/**
* Bind a variable as parameter. The value of the variable will be evaluated when the query is executed.
* This method allows you to supply an array of parameters for $param to bind multiple parameters at once that
* should have the same variable. If an array of parameter identifiers is supplied for $param, $var will be bound to
* all parameters. Parameters that already exists will be overwritten.
*
* @param string|int|array $param Parameter identifier, or array of parameter identifiers.
* @param mixed &$var Parameter variable.
*
* @return int Amount of parameters that where bound successfully.
*/
public function bindParam($param, &$var) {
// Make sure $param isn't null
if($param === null)
return 0;
// Check whether $param should be handled as an array
if(!is_array($param)) {
// Make sure the parameter identifier is valid
if(!self::isValidParamIdentifier($param))
return false;
// Store the parameter variable reference
$this->params[$param] = &$var;
return 1;
} else {
// Count the amount of parameters that where bound successfully
$count = 0;
// Bind each parameter
for($i = 0; $i < sizeof($param); $i++)
// Bind the parameter and increase $count if the parameter was bound successfully
$count += $this->bindParam($param[$i], $var);
// Return the amount of parameters that where bound successfully
return $count;
}
}
/**
* Bind a value as parameter. This method allows you to supply an array of parameters for $param and a array of
* values for $var to bind multiple parameters or values at once. If an array of parameter identifiers is supplied
* for $param and $var is a single value, the value will be bound to all parameters. If an array of parameter
* identifiers is supplied for $param and an array of values is supplied for $var each parameter will get it's own
* value. If the $val array has less elements than the $param value, the first value will be bound to all
* parameters. Parameters that already exists will be overwritten.
*
* @param string|int|array $param Parameter identifier, or array of parameter identifiers.
* @param mixed|array $val Parameter variable, or an array of parameter values.
*
* @return int Amount of parameters that where bound successfully.
*/
public function bindParamValue($param, $val) {
// Make sure $param isn't null
if($param === null)
return 0;
// Check whether $param should be handled as an array
if(!is_array($param)) {
// Make sure the parameter identifier is valid
if(!self::isValidParamIdentifier($param))
return false;
// Make sure $val isn't an array, use the first element of the array if this is the case, null will be used
// if the array was empty
if(is_array($val)) {
if(sizeof($val) > 0)
$val = $val[0];
else
$val = null;
}
// Store the parameter value
$this->params[$param] = $val;
return 1;
} else {
// Count the amount of parameters that where bound successfully
$count = 0;
// Determine whether $val should be handled as array
$valAsArray = false;
if(is_array($val))
if(sizeof($val) >= sizeof($param))
$valAsArray = true;
// Bind each parameter
for($i = 0; $i < sizeof($param); $i++) {
// Get the current parameter entry
$entry = $param[$i];
// Get the value for the current parameter entry
if($valAsArray)
$entryVal = $val[$i];
else
$entryVal = $val;
// Bind the parameter and increase $count if the parameter was bound successfully
$count += $this->bindParamValue($entry, $entryVal);
}
// Return the amount of parameters that where bound successfully
return $count;
}
}
/**
* Get the value of a parameter. Variable parameters will return their value and will be evaluated at the time this
* method is being called. This method also allows you to supply an array of parameter values, in this case an array
* with parameter values will be returned.
*
* @param string|int|array $param Parameter identifier or an array of parameter identifiers.
*
* @return mixed|array|null The value of the parameter. An array with all parameter values will be returned if an
* array of parameters was supplied, some of these values may be null if the corresponding parameter identifier was
* invalid or unknown. Null will be returned if the parameter identifier is invalid or unknown.
*/
public function getParam($param) {
// Make sure $param isn't null
if($param === null)
return null;
// Check whether $param should be handled as a parameter or an array of parameters
if(!is_array($param)) {
// Make sure this parameter is bound, if not, return null
if(!$this->isParam($param))
return null;
// Get and return the parameter value
return $this->params[$param];
} else {
// Create an array to store the output in
$output = Array();
// Get the value of each parameter
foreach($param as $entry)
// Get the value for this parameter and push it into the output array
array_push($output, $this->getParam($entry));
// Return the output array
return $output;
}
}
/**
* Get a list of all bound parameters
*
* @return array Array of all bound parameters
*/
public function getParams() {
return $this->params;
}
/**
* Get the count of bound parameters.
*
* @return int Count of bound parameters
*/
public function getParamCount() {
return sizeof($this->params);
}
/**
* Check whether a parameter is bound
*
* @param string|int $param Parameter identifier
*
* @return bool True if this parameter has been bound, false otherwise
*/
public function isParam($param) {
// Make sure the parameter identifier is valid
if(!self::isValidParamIdentifier($param))
return false;
// Check whether this param or value is bound, return the result
return array_key_exists($param, $this->params);
}
/**
* Determine whether a parameter identifier is valid.
* The parameter identifier must be a string or integer. A string identifier must have at least one character,
* and may not contain any whitespace characters. An integer identifier must be 1 or greater.
*
* @param string|int $param Parameter identifier to check.
*
* @return bool True if the parameter identifier is valid, false otherwise
*/
public function isValidParamIdentifier($param) {
// Check whether the parameter key is a string, if so, make sure it doesn't contain any whitespace characters
if(is_string($param))
return !StringUtils::containsWhitespaces($param, false);
// Check whether the parameter key is an integer, if so, make sure the integer is 1 or greater
if(is_int($param))
return ($param > 0);
// The parameter key doesn't seem to be a string or integer, return false
return false;
}
/**
* Check whether an object is a valid Database Statement. A comprehensive check also ensures the contents of the
* database statement are valid. This method might be expensive if a comprehensive check is done.
*
* @param mixed $statement Database statement object.
* @param bool $comprehensiveCheck True to do a comprehensive check which ensures the contents of the database
* statement are valid, false to skip this check. Using comprehensive checks might be expensive.
*
* @return bool True if the $statement object is a valid Database Statement, false otherwise. False will be returned
* if $statement equals null.
*/
public static function isValid($statement, $comprehensiveCheck = false) {
// Make sure the statement is valid
if($statement === null)
return false;
if(!($statement instanceof DatabaseStatement))
return false;
// Check whether we should do a comprehensive check
if($comprehensiveCheck) {
// TODO: Do a comprehensive check by checking whether the contents of the database statement are valid!
}
// The database statement seems to be valid, return the result
return true;
}
/*public static function parse() {
}
// TODO: Execute method, to run the (prepared) statement, allow additional parameters as function arguments!
public abstract function execute();
// TODO: Rename this to getStatement?
public abstract function getStatement();*/
private $statement = '';
public function __construct($statement = '') {
$this->statement = $statement;
}
}
<?php
namespace carbon\core\database\driver\mysql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Database extends \carbon\core\database\lib\pdo\Database { }
<?php
namespace carbon\core\database\driver\mysql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DatabaseConnector extends \carbon\core\database\lib\pdo\DatabaseConnector {
/**
* Constructor
*
* @param string $host Database host
* @param int|null $port Database port
* @param string|null $user Database username
* @param string|null $pass Database password
* @param string $dbname Database name
* @param bool $connect True to automatically connect
*/
public function __construct($host = self::DEF_HOST, $port = self::DEF_PORT, $user = self::DEF_USER, $pass = self::DEF_PASS, $dbname = self::DEF_DBNAME, $connect = true) {
// Set all properties
$this->setHost($host);
$this->setPort($port);
$this->setUser($user);
$this->setPass($pass);
$this->setDatabaseName($dbname);
// Auto connect
if($connect)
$this->connect();
}
/**
* Get the PDO DSN required to connect any database using PDO.
*
* @return string PDO DSN
*/
public function getDsn() {
$dsn = 'mysql:';
// Append the host
$dsn .= 'host=' . $this->getHost();
// Append the port if specified
if($this->getPort() !== null)
$dsn .= ';port=' . $this->getPort();
// Append the database name
$dsn .= ';dbname=' . $this->getDatabaseName();
// Return the DNS
return $dsn;
}
}
<?php
namespace carbon\core\database\driver\mysql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DatabaseStatement extends \carbon\core\database\lib\pdo\DatabaseStatement { }
[driver]
id = mysql
name = MySQL
namespace = carbon\core\driver\mysql
version_code = 1
version_name = 0.1
<?php
namespace carbon\core\database\lib\pdo;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class Database extends \carbon\core\database\lib\sql\Database { }
<?php
namespace carbon\core\database\lib\pdo;
use PDO;
use PDOException;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseConnector extends \carbon\core\database\lib\sql\DatabaseConnector {
/** @var PDO|null $pdo PDO connection instance */
protected $pdo = null;
/**
* Connect to the database
* @return bool True if succeed, false otherwise
*/
public function connect() {
// Get the PDO DSN, username and password
$dsn = $this->getDsn();
$user = $this->getUser();
$pass = $this->getPass();
try {
// Connect to the database using PDO
if($user !== null && $pass !== null)
$this->pdo = new PDO($dsn, $user, $pass);
else if($user !== null)
$this->pdo = new PDO($dsn, $user);
else
$this->pdo = new PDO($dsn);
// Return the result
return true;
} catch(PDOException $ex) {
// Catch connection errors, reset the PDO state and return the result
$this->pdo = null;
return false;
}
}
/**
* Disconnect from the database
*/
public function disconnect() {
// Disconnect PDO by destroying it's object, set the instance to null afterwards
unset($this->pdo);
$this->pdo = null;
}
/**
* Check whether there'statements an active connection
* @return bool True if connected, false otherwise
*/
public function isConnected() {
return ($this->pdo !== null && $this->pdo instanceof PDO);
}
/**
* Get the PDO DSN required to connect any database using PDO
* @return string PDO DSN
*/
public abstract function getDsn();
// TODO: Should these transaction methods bellow be here, or should they be put somewhere else?
/**
* Start a transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionStart() {
return $this->pdo->beginTransaction();
}
/**
* Commit the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionCommit() {
return $this->pdo->commit();
}
/**
* Rollback the current transaction.
*
* @return bool True on success, false on failure.
* Returns false if transitions aren't supported.
*/
public function transactionRollBack() {
return $this->pdo->rollBack();
}
/**
* Check whether we're inside a transaction.
*
* @return bool True if we're inside a transaction, false otherwise.
*/
public function isInTransaction() {
return $this->pdo->inTransaction();
}
/**
* Check whether this connector has transaction support.
*
* @return bool True if this connector has transaction support, false otherwise
*/
public function isTransactionSupported() {
// TODO: Make sure PDO supports transactions with the current driver!
return true;
}
}
<?php
namespace carbon\core\database\lib\pdo;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseStatement extends \carbon\core\database\lib\sql\DatabaseStatement { }
<?php
namespace carbon\core\database\lib\sql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class Database extends \carbon\core\database\Database { }
<?php
namespace carbon\core\database\lib\sql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseConnector extends \carbon\core\database\DatabaseConnector { }
<?php
namespace carbon\core\database\lib\sql;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class DatabaseStatement extends \carbon\core\database\DatabaseStatement { }
<?php
/**
* DateTime.php
*
* A class to representation date and time as an object.
* This class allows you to get the current date and time of the server, to format the date and time in many different
* ways using different timezones and also to travel through time.
* Note: Even though this class uses futuristic technology to make date and time calculations, it doesn't allow humans
* to travel through time.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Carbon CMS 2015. All rights reserved.
*/
namespace carbon\core\datetime;
use carbon\core\datetime\interval\DateInterval;
use carbon\core\datetime\period\DatePeriod;
use carbon\core\datetime\zone\DateTimeZone;
use carbon\core\datetime\zone\DateTimeZoneUtils;
use carbon\core\util\StringUtils;
use Closure;
use DateTime as PHPDateTime;
use DateTimeZone as PHPDateTimeZone;
use Exception;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* A class to represent, format and transform date and time.
*
* Used the Carbon DateTime library as basis for this class.
*
* @package carbon\core\datetime
*
* @TODO Should we keep these properties, they were removed from the DateInterval class?
*
* @property int $year The year.
* @property int $yearIso The ISO year.
* @property int $month The month.
* @property int $day The day.
* @property int $hour The hour.
* @property int $minute The minute.
* @property int $second The second.
* @property int $timestamp seconds since the Unix Epoch
* @property DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone A DateTimeZone or PHPDateTimeZone
* instance, the timezone ID as a string, a DateTime or PHPDateTime instance to use it's timezone or null to use
* the default timezone.
* @property DateTimeZone $tz Alias of $timezone.
* @property-read int $micro Read the number of micro seconds.
* @property-read int $dayOfWeek Get the day of the week as a number, 0 (for Sunday) through 6 (for Saturday).
* @property-read int $dayOfYear Get the day of the year, 0 through 365.
* @property-read int $weekOfMonth Get the week of the month, 1 through 5.
* @property-read int $weekOfYear Get the ISO-8601 week number of year, weeks starting on Monday.
* @property-read int $daysInMonth Get the number of days in the given month.
* @property-read int $age Get the number of years passed since now().
* @property-read int $quarter Get the the quarter of this instance, from 1 to 4.
* @property-read int $offset Get the timezone offset in seconds from UTC.
* @property-read int $offsetHours Get the timezone offset in hours from UTC.
* @property-read bool $dst Check whether daylight saving time is enabled, true if DST, false otherwise.
* @property-read bool $local Check whether the timezone is local, true if local, false otherwise.
* @property-read bool $utc Check whether the timezone is UTC, true if UTC, false otherwise.
* @property-read string $timezoneName Get the timezone name or ID.
* @property-read string $tzName Alias for $timezoneName.
*/
class DateTime extends PHPDateTime {
// TODO: Does this class have support for all date's, (an almost-infinite range)!
// TODO: Return null on failure, instead of false, for everything?
// TODO: Create a class for DateInterval, with the possibility to add or subtract from this date and time.
// TODO: Option to set a preferred parsing format?
// TODO: Support for all types of calendars!
// TODO: Add the possibility to extend this class, with some functions, closures and so on.
// TODO: Make sure the mock DateTime is used everywhere it's needed, such as the now(); method!
// TODO: Add proper exceptions for everything!
// TODO: Should we overwrite all/most methods provided by the base class.
// TODO: Should we name these differently, because the name is currently the same as PHP's classes
// TODO: Make sure all DateTime and PHPDateTime instances are correct!
// TODO: Rename subtract methods to sub
/**
* The day constant for sunday, this defines an integer for this week day.
*
* @const int Day index.
*/
const SUNDAY = 0;
/**
* The day constant for monday, this defines an integer for this week day.
*
* @const int Day index.
*/
const MONDAY = 1;
/**
* The day constant for tuesday, this defines an integer for this week day.
*
* @const int Day index.
*/
const TUESDAY = 2;
/**
* The day constant for wednesday, this defines an integer for this week day.
*
* @const int Day index.
*/
const WEDNESDAY = 3;
/**
* The day constant for thursday, this defines an integer for this week day.
*
* @const int Day index.
*/
const THURSDAY = 4;
/**
* The day constant for friday, this defines an integer for this week day.
*
* @const int Day index.
*/
const FRIDAY = 5;
/**
* The day constant for saturday, this defines an integer for this week day.
*
* @const int Day index.
*/
const SATURDAY = 6;
/**
* Define the names of each weekday as an array, indexed by the day constants.
*
* @var Array An array containing all weekday names.
*/
protected static $DAY_NAMES = array(
self::SUNDAY => 'Sunday',
self::MONDAY => 'Monday',
self::TUESDAY => 'Tuesday',
self::WEDNESDAY => 'Wednesday',
self::THURSDAY => 'Thursday',
self::FRIDAY => 'Friday',
self::SATURDAY => 'Saturday'
);
/**
* Terms used to detect if a time passed is a relative date for testing purposes.
*
* @var Array An array of relative keywords.
*/
protected static $RELATIVE_KEYWORDS = Array(
'this',
'next',
'last',
'tomorrow',
'yesterday',
'+',
'-',
'first',
'last',
'ago'
);
/**
* Defines the format to use for most getter names.
*
* @var Array An array of getter formats.
*/
protected static $GETTER_FORMATS = Array(
'year' => 'Y',
'yearIso' => 'o',
'month' => 'n',
'day' => 'j',
'hour' => 'G',
'minute' => 'i',
'second' => 's',
'micro' => 'u',
'dayOfWeek' => 'w',
'dayOfYear' => 'z',
'weekOfYear' => 'W',
'daysInMonth' => 't',
'timestamp' => 'U'
);
// TODO: Move this stuff to a DateTimeUnits class?
/**
* Defines the years per century, for time calculations.
*
* @const int Years per century.
*/
const YEARS_PER_CENTURY = 100;
/**
* Defines the years per decade, for time calculations.
*
* @const int Years per decade.
*/
const YEARS_PER_DECADE = 10;
/**
* Defines the months per year, for time calculations.
*
* @const int Months per year.
*/
const MONTHS_PER_YEAR = 12;
/**
* Defines the weeks per year, for time calculations.
*
* @const int Weeks per year.
*/
const WEEKS_PER_YEAR = 52;
/**
* Defines the days per week, for time calculations.
*
* @const int Days per week.
*/
const DAYS_PER_WEEK = 7;
/**
* Defines the hours per day, for time calculations.
*
* @const int Hours per day.
*/
const HOURS_PER_DAY = 24;
/**
* Defines the minutes per hour, for time calculations.
*
* @const int Minutes per hour.
*/
const MINUTES_PER_HOUR = 60;
/**
* Defines the seconds per minute, for time calculations.
*
* @const int Seconds per minute.
*/
const SECONDS_PER_MINUTE = 60;
/**
* Defines the default date format used when date and time is represented as a string.
*
* @const string The default date and time format.
*/
const DEFAULT_FORMAT = 'Y-m-d H:i:s';
/**
* Defines the default date format used when a date is represented as a string.
*
* @const string The default date format.
*/
const DEFAULT_FORMAT_DATE = 'Y-m-d';
/**
* Defines the default time format used when time is represented as a string.
*
* @const string The default time format.
*/
const DEFAULT_FORMAT_TIME = 'H:i:s';
/**
* Defines the default date and time format that includes everything in the string.
*
* @const string The default complete date and time format.
*/
// TODO: Give this constant a proper name!
const DEFAULT_FORMAT_COMPLETE = 'Y-m-d H:i:s.u e O';
/**
* An optional mock DateTime instance to return when the now() method is called.
*
* @var DateTime The mock DateTime instance, or null to use the default.
*/
protected static $mockNow;
/**
* Constructor.
*
* @param string|DateTime|PHPDateTime $dateTime [optional] The date and time as a string, the date and time as
* DateTime or PHPDateTime instance or null to use the current time.
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime $timezone [optional] The timezone the specified
* time is in, or null to use the default timezone. A DateTime or PHPDateTime instance to use it's timezone.
*
* @throws Exception Throws an exception on failure.
*/
public function __construct($dateTime = null, $timezone = null) {
// Parse the timezone if it isn't null and make sure it's valid
if($timezone !== null)
if(($timezone = DateTimeZoneUtils::parse($timezone, null)) === null)
// TODO: Add a better exception, with a better description!
throw new Exception('Invalid timezone!');
// Handle DateTime instances
if($dateTime instanceof self) {
// Construct the parent object with the proper properties
parent::__construct($dateTime->toCompleteString(), $dateTime->getTimezone());
return $this;
}
// Handle PHPDateTime instances
if($dateTime instanceof parent) {
// Construct the parent object with the proper properties
parent::__construct($dateTime->format(self::DEFAULT_FORMAT_COMPLETE), $dateTime->getTimezone());
return $this;
}
// Check whether we should use the now time
if(empty($dateTime) || StringUtils::equals($dateTime, 'now', false, true))
// Return a new instance of the mock time if set
if(static::hasMockNow())
return clone static::getMockNow();
// Check whether the time contains relative keywords
if(static::hasRelativeKeywords($dateTime)) {
// Get a new DateTime instance, and modify the date and time according to the time parameter
$dateTime = static::now()->modify($dateTime);
// Shift the timezone if it's set
if($timezone !== null && !$timezone->equals(static::getMockNow()))
$dateTime->setTimezone($timezone);
else
$timezone = $dateTime->getTimezone();
// Update the time parameter with the modified time
$dateTime = $dateTime->toCompleteString();
}
// Construct the parent object
parent::__construct($dateTime, $timezone);
return $this;
}
/**
* Create a copy of a DateTime instance.
*
* @param DateTime $other The other instance.
*
* @return static The new DateTime instance.
*/
// TODO: Parse the parameter for better flexibility, make sure an instance isn't created twice.
public static function instance(self $other) {
return new static($other->format(self::DEFAULT_FORMAT_COMPLETE), $other->getTimeZone());
}
/**
* Create a copy of this DateTime instance.
*
* @return static A new DateTime zone instance.
*/
public function copy() {
return static::instance($this);
}
/**
* Create a copy of this DateTime instance. Called by PHP when the DateTime object is cloned.
*
* @return static A new DateTime zone instance
*/
public function __clone() {
return $this->copy();
}
/**
* Parse date and time with a specific timezone. A new instance may be created if required.
*
* If the $time parameter is a DateTime zone instance, the instance will be returned and the $timezone parameter is
* ignored. If the $time parameter is anything other than a DateTime zone the date, time and the timezone is parsed
* through the constructor.
*
* This method allows better fluent syntax because it makes method chaining possible.
*
* @param DateTime|string|null $dateTime [optional] A DateTime instance, the time as a string, or null to use the
* current time.
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone [optional] The timezone the
* specified time is in, or null to use the default timezone if the $time param isn't a DateTime instance. A
* DateTime or PHPDateTime instance to use it's timezone.
*
* @return DateTime|null The parsed DateTime instance, or null on failure.
*/
// TODO: Should we move the parse code to this class, instead of the utils class?
public static function parse($dateTime = null, $timezone = null) {
return DateTimeUtils::parse($dateTime, $timezone, null);
}
/**
* Create a DateTime instance for the current date and time.
*
* @param DateTimeZone|PHPDateTimeZone|string $timezone [optional] The preferred timezone to use, or null to use
* the default timezone.
* @param bool $real [optional] True to return the real now() value which ignores the mock date and time, false to
* return the normal value.
*
* @return static The DateTime instance.
*/
public static function now($timezone = null, $real = false) {
// Check whether the regular or the real now should be returned
if(!$real)
return new static(null, $timezone);
// Return the real now time
return new static(new parent('now', $timezone), $timezone);
}
/**
* Create a DateTime instance for the start of the current day.
*
* @param DateTimeZone|PHPDateTimeZone|string $timezone [optional] The preferred timezone to use, or null to use
* the default timezone.
* @param bool $real [optional] True to use the real now() value which ignores the mock date and time, false to
* return the normal value.
*
* @return static The DateTime instance.
*/
public static function today($timezone = null, $real = false) {
return static::now($timezone, $real)->startOfDay();
}
/**
* Create a DateTime instance for the start of tomorrow.
*
* @param DateTimeZone|PHPDateTimeZone|string $timezone [optional] The preferred timezone to use, or null to use
* the default timezone.
* @param bool $real [optional] True to use the real now() value which ignores the mock date and time, false to
* return the normal value.
*
* @return static The DateTime instance.
*/
public static function tomorrow($timezone = null, $real = false) {
return static::today($timezone, $real)->addDay();
}
/**
* Create a DateTime instance for the start of yesterday.
*
* @param DateTimeZone|PHPDateTimeZone|string $timezone [optional] The preferred timezone to use, or null to use
* the default timezone.
* @param bool $real [optional] True to use the real now() value which ignores the mock date and time, false to
* return the normal value.
*
* @return static The DateTime instance.
*/
public static function yesterday($timezone = null, $real = false) {
return static::today($timezone, $real)->subtractDay();
}
/**
* Create a DateTime instance for the greatest supported date and time.
*
* @return static The DateTime instance.
*/
public static function greatestDate() {
return static::createFromTimestamp(PHP_INT_MAX);
}
/**
* Create a DateTime instance for the lowest supported date and time.
*
* @return static The DateTime instance.
*/
public static function lowestDate() {
return static::createFromTimestamp(~PHP_INT_MAX);
}
/**
* Create a new DateTime instance from a specific date and time.
*
* If the $year, $month or $day parameters are set to null their now() value will be used.
*
* If $hour is null it will be set to its now() value and the default values for $minute and $second will be their
* now() values. If $hour is not null, then the default values for $minute and $second will be 0.
*
* @param int $year [optional] The specified year, or null to use the current year.
* @param int $month [optional] The specified month, or null to use the current month.
* @param int $day [optional] The specified day, or null to use the current day.
* @param int $hour [optional] The specified hour, or null to use the current hour.
* @param int $minute [optional] The specified minute, or null.
* @param int $second [optional] The specified second, or null.
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone [optional] The preferred timezone
* to use, or null to use the default timezone. A DateTime or PHPDateTime instance to use it's timezone.
*
* @return static The DateTime instance.
*/
// TODO: Does this method return null on some sort of failure?
// TODO: Redo this method!
// TODO: Does this only return full hours when no parameter is given?
public static function create($year = null, $month = null, $day = null, $hour = null, $minute = null,
$second = null, $timezone = null) {
// Specify the date and time parts
$year = $year === null ? date('Y') : $year;
$month = $month === null ? date('n') : $month;
$day = $day === null ? date('j') : $day;
$hour = $hour === null ? date('G') : $hour;
$minute = $minute === null ? ($hour === null ? date('i') : 0) : $minute;
$second = $second === null ? ($hour === null ? date('s') : 0) : $second;
// Create a DateTime instance from the time parts
return static::createFromFormat('Y-n-j G:i:s',
sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $timezone);
}
/**
* Create a DateTime instance with a specific date. The time portion is set to now.
*
* If the $year, $month or $day parameters are set to null their now() value will be used.
*
* @param int $year [optional] The specified year, or null to use the current year.
* @param int $month [optional] The specified month, or null to use the current month.
* @param int $day [optional] The specified day, or null to use the current day.
* @param DateTimeZone|PHPDateTimeZone|string $timezone The preferred timezone to use, or null to use the default
* timezone.
*
* @return static The DateTime instance.
*/
public static function createFromDate($year = null, $month = null, $day = null, $timezone = null) {
return static::create($year, $month, $day, null, null, null, $timezone);
}
/**
* Create a DateTime instance with a specific time. The date portion is set to today.
*
* If $hour is null it will be set to its now() value and the default values for $minute and $second will be their
* now() values. If $hour is not null, then the default values for $minute and $second will be 0.
*
* @param int $hour [optional] The specified hour, or null to use the current hour.
* @param int $minute [optional] The specified minute, or null.
* @param int $second [optional] The specified second, or null.
* @param DateTimeZone|PHPDateTimeZone|string $timezone The preferred timezone to use, or null to use the default
* timezone.
*
* @return static The DateTime instance.
*/
public static function createFromTime($hour = null, $minute = null, $second = null, $timezone = null) {
return static::create(null, null, null, $hour, $minute, $second, $timezone);
}
/**
* Create a DateTime instance from a string with a specified format.
*
* @param string $format The format used to parse the date time.
* @param string $time The date time to parse as a string.
* @param DateTimeZone|PHPDateTimeZone|string $timezone The preferred timezone to use, or null to use the default
* timezone.
*
* @return static The DateTime instance or null on failure.
*/
// TODO: Review this method!
public static function createFromFormat($format, $time, $timezone = null) {
// Try to create a DateTime instance based on the input
if($timezone !== null)
$dateTime = parent::createFromFormat($format, $time, DateTimeZoneUtils::safeCreateDateTimeZone($timezone));
else
$dateTime = parent::createFromFormat($format, $time);
// Make sure the object is valid
if($dateTime === false)
return null;
// Parse and return the date time
return self::parse($dateTime, $timezone);
}
/**
* Create a DateTime instance based on a Unix timestamp.
*
* @param int $timestamp The timestamp to get the DateTime instance for.
* @param DateTimeZone|PHPDateTimeZone|string $timezone The preferred timezone to use, or null to use the default
* timezone.
*
* @return static The DateTime instance, or null on failure.
*/
public static function createFromTimestamp($timestamp, $timezone = null) {
// Create a DateTime instance and make sure it's valid
if(($dateTime = static::now($timezone)->setTimestamp($timestamp)) === false)
return null;
// Return the DateTime instance
return $dateTime;
}
/**
* Create a DateTime instance based on a UTC timestamp.
*
* @param int $timestamp The UTC timestamp.
*
* @return static The DateTime instance.
*/
public static function createFromTimestampUTC($timestamp) {
return new static('@' . $timestamp);
}
/**
* Get the year.
*
* @return int The year.
*/
public function getYear() {
return (int) $this->format(static::$GETTER_FORMATS['year']);
}
/**
* Change the year.
*
* @param int $year The year.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setYear($year) {
// Set the year
try {
$this->year = $year;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the month.
*
* @return int The month.
*/
public function getMonth() {
return (int) $this->format(static::$GETTER_FORMATS['month']);
}
/**
* Change the month.
*
* @param int $month The month.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setMonth($month) {
// Set the month
try {
$this->month = $month;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the day.
*
* @return int The day.
*/
public function getDay() {
return (int) $this->format(static::$GETTER_FORMATS['day']);
}
/**
* Change the day.
*
* @param int $day The day.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setDay($day) {
// Set the day
try {
$this->day = $day;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the hour.
*
* @return int The hour.
*/
public function getHour() {
return (int) $this->format(static::$GETTER_FORMATS['hour']);
}
/**
* Change the hour.
*
* @param int $hour The hour.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setHour($hour) {
// Set the hour
try {
$this->hour = $hour;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the minute.
*
* @return int The minute.
*/
public function getMinute() {
return (int) $this->format(static::$GETTER_FORMATS['minute']);
}
/**
* Change the minute.
*
* @param int $minute The minute.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setMinute($minute) {
// Set the minute
try {
$this->minute = $minute;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the second.
*
* @return int The second.
*/
public function getSecond() {
return (int) $this->format(static::$GETTER_FORMATS['second']);
}
/**
* Change the second.
*
* @param int $second The second.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setSecond($second) {
// Set the second
try {
$this->second = $second;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Set the timestamp.
*
* @param int $timestamp The timestamp.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setTimestamp($timestamp) {
// Set the timestamp
try {
$this->timestamp = $timestamp;
return $this;
} catch(Exception $ex) {
return null;
}
}
/**
* Get the timezone.
*
* @return DateTimeZone|null The timezone, or null on failure.
*/
public function getTimezone() {
return DateTimeZoneUtils::parse(parent::getTimezone());
}
/**
* Set the timezone.
*
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime $timezone A DateTimeZone or PHPDateTimeZone
* instance or the timezone ID as a string. A DateTime or PHPDateTime instance to use it's timezone.
*
* @return static|null A DateTime instance on success for method chaining, null on failure.
*/
public function setTimezone($timezone) {
// Parse the timezone, return null on failure
if(($timezone = DateTimeZoneUtils::parse($timezone)) === null)
return null;
// Set the timezone, and return the current instance for method chaining
parent::setTimezone($timezone);
return $this;
}
/**
* Get a mock DateTime instance which is returned when the now() method is called.
*
* @return DateTime The mock DateTime instance, or null when no mock instance is set.
*/
public static function getMockNow() {
return static::$mockNow;
}
/**
* Check whether a mock DateTime instance is set.
*
* @return bool True if any mock DateTime instance is set, false otherwise.
*/
public static function hasMockNow() {
return static::getMockNow() !== null;
}
/**
* Set a mock DateTime instance which is returned when the now() method is called.
* A new DateTime instance will be created if $mockNow needs to be parsed into a DateTime instance.
* This affects all methods using the now() method as default when no time data is supplied.
*
* The timezone doesn't have any effect on this method.
*
* To reset the mock instance, call this method using the default parameter of null.
*
* @param DateTime|PHPDateTime|string|null $mockNow [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to reset the mock date and time.
*
* @return bool True on success, false on failure.
*/
public static function setMockNow($mockNow = null) {
// Check whether the mock instance should be reset
if($mockNow === null) {
static::$mockNow = null;
return true;
}
// Parse the mock date and time, return false on failure
if(($mockNow = static::parse($mockNow)) === null)
return false;
// Set the mock instance, return the result
static::$mockNow = $mockNow;
return true;
}
/**
* Get the micro.
*
* @return int The micro.
*/
public function getMicro() {
return (int) $this->format(static::$GETTER_FORMATS['micro']);
}
/**
* Get the day of the week.
*
* @return int The day of the week.
*/
public function getDayOfWeek() {
return (int) $this->format(static::$GETTER_FORMATS['dayOfWeek']);
}
/**
* Get the day of the year.
*
* @return int The day of the year.
*/
public function getDayOfYear() {
return (int) $this->format(static::$GETTER_FORMATS['dayOfYear']);
}
/**
* Get the week of the year.
*
* @return int The week of the year.
*/
public function getWeekOfYear() {
return (int) $this->format(static::$GETTER_FORMATS['weekOfYear']);
}
/**
* Get the number of days in this month.
*
* @return int The number of days.
*/
public function getDaysInMonth() {
return (int) $this->format(static::$GETTER_FORMATS['daysInMonth']);
}
/**
* Set the date of the object.
*
* @param int|null $year [optional] The year, or null to leave the year unchanged.
* @param int|null $month [optional] The month, or null to leave the month unchanged.
* @param int|null $day [optional] The day, or null to leave the day unchanged.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function setDate($year = null, $month = null, $day = null) {
// Make sure the parameters are valid or null
if(($year !== null && !is_int($year)) || ($month !== null && !is_int($month)) ||
($day !== null && !is_int($day))
)
return null;
// Handle the null parameters
if($year === null)
$year = $this->getYear();
if($month === null)
$month = $this->getMonth();
if($day === null)
$day = $this->getDay();
// Set the date using the parent function, return this or null on failure
return parent::setDate($year, $month, $day) === false ? null : $this;
}
/**
* Set the time of the object.
*
* @param int|null $hour [optional] The hour, or null to leave the hour unchanged.
* @param int|null $minute [optional] The minute, or null to leave the minute unchanged.
* @param int|null $second [optional] The second, or null to leave the second unchanged.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function setTime($hour = null, $minute = null, $second = null) {
// Make sure the parameters are valid or null
if(($hour !== null && !is_int($hour)) || ($minute !== null && !is_int($minute)) ||
($second !== null && !is_int($second))
)
return null;
// Handle the null parameters
if($hour === null)
$hour = $this->getHour();
if($minute === null)
$minute = $this->getMinute();
if($second === null)
$second = $this->getSecond();
// Set the time using the parent function, return this or null on failure
return parent::setTime($hour, $minute, $second) === false ? null : $this;
}
/**
* Get a part of the DateTime object.
*
* @param string $name The getter name.
*
* @return string|int|DateTimeZone|PHPDateTimeZone
*
* @throws \InvalidArgumentException
*/
// TODO: Handle these getters through the real get... setters!
public function __get($name) {
// Parse the getter based on the getter formats list
if(array_key_exists($name, static::$GETTER_FORMATS))
return (int) $this->format(static::$GETTER_FORMATS[$name]);
// Get the week number of the month
if(StringUtils::equals($name, 'weekOfMonth', false))
return (int) ceil($this->day / static::DAYS_PER_WEEK);
// Get the age
if(StringUtils::equals($name, 'age', false))
return $this->getAge();
// Ge the current quarter number
if(StringUtils::equals($name, 'quarter', false))
return (int) ceil($this->month / 3);
// Get the offset
if(StringUtils::equals($name, 'offset', false))
return $this->getOffset();
// Get the offset hours
if(StringUtils::equals($name, 'offsetHours', false))
return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR;
// Check whether daylight saving time is active
if(StringUtils::equals($name, 'dst', false))
return $this->format('I') == '1';
// Check if the timezone is local
if(StringUtils::equals($name, 'local', false))
return $this->getTimezone()->isLocal(DateTimeZoneUtils::getDefaultTimezone());
// Check whether the time is in UTC
if(StringUtils::equals($name, 'utc', false))
return $this->offset == 0;
// Get the timezone
if(StringUtils::equals($name, Array('timezone', 'tz'), false))
return $this->getTimezone();
// Get the timezone name
if(StringUtils::equals($name, Array('timezoneName', 'tzName'), false))
return $this->getTimezone()->getName();
// The field name is unknown, throw an exception
// TODO: Should we throw an error, or is returning nothing fine?
throw new \InvalidArgumentException('Unknown getter \'' . $name . '\'');
}
/**
* Check if an attribute exists on the object.
*
* @param string $name The name of the attribute.
*
* @return bool True if the attribute exists, false otherwise.
*/
public function __isset($name) {
try {
// Try to get an attribute
$this->__get($name);
} catch(\InvalidArgumentException $e) {
// The attribute doesn't exist, return the result
return false;
}
// The attribute does exist, return the result
return true;
}
/**
* Set a part of the DateTime object.
*
* @param string $name The name of the attribute to set.
* @param string|int|DateTimeZone|PHPDateTimeZone $value The value to set it to.
*
* @throws Exception|\InvalidArgumentException Throws an exception on failure.
*/
// TODO: Handle these setters through the real set... setters!
public function __set($name, $value) {
// Set the year
if(StringUtils::equals($name, 'year', true)) {
if($this->setDate($value, null, null) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the month
if(StringUtils::equals($name, 'month', true)) {
if($this->setDate(null, $value, null) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the day
if(StringUtils::equals($name, 'day', true)) {
if($this->setDate(null, null, $value) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the hour
if(StringUtils::equals($name, 'hour', true)) {
if($this->setTime($value, null, null) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the minute
if(StringUtils::equals($name, 'minute', true)) {
if($this->setTime(null, $value, null) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the second
if(StringUtils::equals($name, 'second', true)) {
if($this->setTime(null, null, $value) === null)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the timestamp
if(StringUtils::equals($name, 'timestamp', true)) {
if(parent::setTimestamp($value) === false)
throw new \DomainException('Invalid type for \'' . $name . '\'');
return;
}
// Set the timezone
if(StringUtils::equals($name, Array('timezone', 'tz'), true)) {
if($this->setTimezone($value) === null)
throw new \DomainException('Invalid type for ' . $name);
return;
}
// Failed to set the attribute, throw an exception
// TODO: Should we throw an error, or is returning nothing fine?
throw new \InvalidArgumentException('Unknown setter \'' . $name . '\'');
}
/**
* Set the date and time of the object.
*
* @param int|null $year [optional] The year, or null to leave the year unchanged.
* @param int|null $month [optional] The month, or null to leave the month unchanged.
* @param int|null $day [optional] The day, or null to leave the day unchanged.
* @param int|null $hour [optional] The hour, or null to leave the hour unchanged.
* @param int|null $minute [optional] The minute, or null to leave the minute unchanged.
* @param int|null $second [optional] The second, or null to leave the second unchanged.
*
* @return static|null The DateTime instance, or null on failure
*/
public function setDateTime($year, $month, $day, $hour, $minute, $second = 0) {
// Set the date, return null on failure
if($this->setDate($year, $month, $day) === null)
return null;
// Set the time and return this, return null on failure
return $this->setTime($hour, $minute, $second) === null ? null : $this;
}
/**
* Check whether there is a relative keyword in the date and time string, this is to create dates relative to now
* for test instances. e.g.: next tuesday
*
* @param string $time The date and time string to check.
*
* @return bool True if there is a relative keyword in the date and time string, false otherwise.
*/
public static function hasRelativeKeywords($time) {
// Check whether the time string contains any relative keywords
// Skip a common time format
if(preg_match('/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/', $time) !== 1)
foreach(static::$RELATIVE_KEYWORDS as $keyword)
if(StringUtils::contains($time, $keyword, false))
return true;
// The time string doesn't contain any relative keywords, return the result
return false;
}
// TODO: Put all translation and to string stuff here...!
/**
* Check whether the date equals to the date of the specified date and time parameter.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The other DateTime or PHPDateTime instance, the
* date and time as a string or null to use the now() time.
*
* @return bool True if the date equals the date of the specified date and time, false otherwise. False will also
* be returned if the specified date and time was invalid.
*/
public function equals($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Compare the date and time of both objects, return the result
return StringUtils::equals($this->toCompleteString(), $dateTime->toCompleteString());
}
/**
* Check whether the date is greater than the date of the specified date and time parameter.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The other DateTime or PHPDateTime instance, the
* date and time as a string or null to use the now() time.
*
* @return bool|null True if the date is greater than the specified date and time, false otherwise. Null will be
* returned if the specified date and time was invalid.
*/
public function isGreaterThan($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Compare the date and time, return the result
return $this > $dateTime;
}
/**
* Check whether the date is greater or equal to the date of the specified date and time parameter.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The other DateTime or PHPDateTime instance, the
* date and time as a string or null to use the now() time.
*
* @return bool|null True if the date is greater or equal to the specified date and time, false otherwise. Null
* will be returned if the specified date and time was invalid.
*/
public function isGreaterOrEqualTo($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Compare the date and time, return the result
return $this >= $dateTime;
}
/**
* Check whether the date is less than the date of the specified date and time parameter.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The other DateTime or PHPDateTime instance, the
* date and time as a string or null to use the now() time.
*
* @return bool|null True if the date is less than the specified date and time, false otherwise. Null will be
* returned if the specified date and time was invalid.
*/
public function isLessThan($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Compare the date and time, return the result
return $this < $dateTime;
}
/**
* Check whether the date is less or equal to the date of the specified date and time parameter.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The other DateTime or PHPDateTime instance, the
* date and time as a string or null to use the now() time.
*
* @return bool|null True if the date is less or equal to the specified date and time, false otherwise. Null will
* be returned if the specified date and time was invalid.
*/
public function isLessOrEqualTo($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Compare the date and time, return the result
return $this <= $dateTime;
}
/**
* Check whether the specified date and time is between a and b.
* The $a and $b parameter may not be null at the same time or false will be returned.
*
* @param DateTime|PHPDateTime|string|null $a The date and time as DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b The date and time as DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param bool $equals [optional] True to also return true if the date equals one of the specified date and times,
* false otherwise.
*
* @return bool|null True if the date is between the specified date and time, or if it equals one of the date and
* times while $equals is set to true. False will be returned otherwise. Null will be returned on failure.
*/
public function isBetween($a = null, $b = null, $equals = true) {
// The a and b parameters may not be null at the same time
if($a === null && $b === null)
return null;
// Parse the date and times, return null on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return null;
// Get the lowest and greatest date
$aGreater = $a->isGreaterThan($b);
$lowest = $aGreater ? $b : $a;
$greatest = $aGreater ? $a : $b;
// Check whether the dates may equal
if($equals)
// Check whether the date is in between or equals, return the result
return $this->isGreaterOrEqualTo($lowest) && $this->isLessOrEqualTo($greatest);
// Check whether the date is in between, return the result
return $this->isGreaterThan($lowest) && $this->isLessThan($greatest);
}
/**
* Get the greatest date and time of this instance and the specified date and time.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return DateTime|null The greatest DateTime instance, or null on failure.
*/
public function max($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = self::parse($dateTime)) === null)
return null;
// Return the greatest date and time
return $this->isGreaterOrEqualTo($dateTime) ? $this : $dateTime;
}
/**
* Get the lowest date and time of this instance and the specified date and time.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return DateTime|null The lowest DateTime instance, or null on failure.
*/
public function min($dateTime = null) {
// Parse the date and time, return false on failure
if(($dateTime = self::parse($dateTime)) === null)
return null;
// Return the greatest date and time
return $this->isLessOrEqualTo($dateTime) ? $this : $dateTime;
}
/**
* Check whether this is a weekday (monday to friday).
*
* @return bool True if this is a weekday, false otherwise.
*/
public function isWeekday() {
return $this->dayOfWeek != static::SUNDAY && $this->dayOfWeek != static::SATURDAY;
}
/**
* Check whether this is a weekend day (saturday or sunday).
*
* @return bool True if this is a weekend day, false otherwise.
*/
public function isWeekend() {
return !$this->isWeekday();
}
/**
* Check whether this is today.
*
* @return bool True if this is today, false if not.
*/
public function isToday() {
return StringUtils::equals($this->toDateString(), static::now($this->getTimezone())->toDateString());
}
/**
* Check whether this is tomorrow.
*
* @return bool True if this is tomorrow, false if not.
*/
public function isTomorrow() {
return StringUtils::equals($this->toDateString(), static::tomorrow($this->getTimezone())->toDateString());
}
/**
* Check whether this is yesterday.
*
* @return bool True if this is yesterday, false if not.
*/
public function isYesterday() {
return StringUtils::equals($this->toDateString(), static::yesterday($this->getTimezone())->toDateString());
}
/**
* Check whether this is in the future. If the date and time equals the now() date and time false is returned.
*
* @return bool True if this is in the future, false if not.
*/
public function isFuture() {
return $this->isGreaterThan(static::now($this->getTimezone()));
}
/**
* Check whether this is in the past. If the date and time equals the now() date and time false is returned.
*
* @return bool True if this is in the past, false if not.
*/
public function isPast() {
return $this->isLessThan(static::now($this->getTimezone()));
}
/**
* Check whether this is a leap year.
*
* @return bool True if this is a leap year, false if not.
*/
public function isLeapYear() {
return StringUtils::equals($this->format('L'), '1');
}
/**
* Check whether the year equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return bool True if year is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameYear($dateTime) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->format('Y'), $dateTime->format('Y'));
}
/**
* Check whether the month equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return bool True if month is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameMonth($dateTime) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->format('Y-m'), $dateTime->format('Y-m'));
}
/**
* Check whether the ISO-8601 week equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return bool True if the ISO-8601 week is the same as the specified date, false otherwise. False will also be
* returned on failure.
*/
public function isSameWeek($dateTime) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->format('Y-W'), $dateTime->format('Y-W'));
}
/**
* Check whether the date is equal to the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
*
* @return bool True if the date is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameDate($dateTime) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Check whether the date is equal
return StringUtils::equals($this->toDateString(), $dateTime->toDateString());
}
/**
* Check whether the hour equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
*
* @return bool True if hour is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameHour($dateTime, $checkDate = true) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Compare the date
if($checkDate)
if(!$this->isSameDate($dateTime))
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->format('H'), $dateTime->format('H'));
}
/**
* Check whether the minute equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
*
* @return bool True if minute is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameMinute($dateTime, $checkDate = true) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Compare the date
if($checkDate)
if(!$this->isSameDate($dateTime))
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->format('H:i'), $dateTime->format('H:i'));
}
/**
* Check whether the second equals the specified date.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
*
* @return bool True if second is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameTime($dateTime, $checkDate = true) {
// Parse the date and time, return false on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return false;
// Compare the date
if($checkDate)
if(!$this->isSameDate($dateTime))
return false;
// Check whether the week is equal, return the result
return StringUtils::equals($this->toTimeString(), $dateTime->toTimeString());
}
/**
* Alter the date and time by incrementing or decrementing based on the modify parameter in a format accepted by
* strtotime(). If an empty string, or null is provided null will be returned.
*
* @param string $modify A date/time string in a format accepted by PHPs strtotime();.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function modify($modify) {
// Make sure the modify parameter isn't empty
if(empty($modify))
return $this;
// Modify the date and time using the parent method, return null on failure
if(parent::modify($modify) === false)
return null;
// Return the current instance for method chaining
return $this;
}
/**
* Travel the specified number of years forward in time. A positive number of years will travel forward in time,
* while a negative number of years travels backward.
*
* @param int $years The number of years to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addYears($years) {
// Make sure the years value is an integer
if(!is_int($years))
return null;
// Travel the specified number of years in time
// TODO: Should we prefix this add sign, or make it dynamic? (For all similar methods)
if($this->modify($years . ' year') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a year, or the specified number of years forward in time. A positive number of years will travel forward
* in time, while a negative number of years travels backward.
*
* @param int $years [optional] The number of years to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addYear($years = 1) {
return $this->addYears($years);
}
/**
* Travel the specified number of years backward in time. A positive number of years will travel backward in time,
* while a negative number of years travels forward.
*
* @param int $years The number of years to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
// TODO: Should we rename these methods to sub..., because that same term is used in PHPs methods!?
public function subYears($years) {
return $this->addYears($years * -1);
}
/**
* Travel a year, or the specified number of years backward in time. A positive number of years will travel
* backward in time, while a negative number of years will travel forward.
*
* @param int $years [optional] The number of years to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subYear($years = 1) {
return $this->subYears($years);
}
/**
* Travel the specified number of months forward in time. A positive number of months will travel forward in time,
* while a negative number of months travels backward.
*
* @param int $months The number of months to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addMonths($months) {
// Make sure the months value is an integer
if(!is_int($months))
return null;
// Travel the specified number of months in time
if($this->modify($months . ' month') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a month, or the specified number of months forward in time. A positive number of months will travel
* forward in time, while a negative number of months travels backward.
*
* @param int $months [optional] The number of months to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addMonth($months = 1) {
return $this->addMonths($months);
}
/**
* Travel the specified number of months backward in time. A positive number of months will travel backward in
* time, while a negative number of months travels forward.
*
* @param int $months The number of months to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subMonths($months) {
return $this->addMonths($months * -1);
}
/**
* Travel a month, or the specified number of months backward in time. A positive number of months will travel
* backward in time, while a negative number of months will travel forward.
*
* @param int $months [optional] The number of months to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subMonth($months = 1) {
return $this->subtractMonths($months);
}
/**
* Travel the specified number of days forward in time. A positive number of days will travel forward in time,
* while a negative number of days travels backward.
*
* @param int $days The number of days to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addDays($days) {
// Make sure the days value is an integer
if(!is_int($days))
return null;
// Travel the specified number of days in time
if($this->modify($days . ' day') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a day, or the specified number of days forward in time. A positive number of days will travel forward in
* time, while a negative number of days travels backward.
*
* @param int $days [optional] The number of days to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addDay($days = 1) {
return $this->addDays($days);
}
/**
* Travel the specified number of days backward in time. A positive number of days will travel backward in time,
* while a negative number of days travels forward.
*
* @param int $days The number of days to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subDays($days) {
return $this->addDays($days * -1);
}
/**
* Travel a day, or the specified number of days backward in time. A positive number of days will travel backward
* in time, while a negative number of days will travel forward.
*
* @param int $days [optional] The number of days to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subDay($days = 1) {
return $this->subtractDays($days);
}
/**
* Travel the specified number of weekdays forward in time. A positive number of weekdays will travel forward in
* time, while a negative number of weekdays travels backward.
*
* @param int $weekdays The number of weekdays to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addWeekdays($weekdays) {
// Make sure the weekdays value is an integer
if(!is_int($weekdays))
return null;
// Travel the specified number of weekdays in time
if($this->modify($weekdays . ' weekday') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a weekday, or the specified number of weekdays forward in time. A positive number of weekdays will travel
* forward in time, while a negative number of weekdays travels backward.
*
* @param int $weekdays [optional] The number of weekdays to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addWeekday($weekdays = 1) {
return $this->addWeekdays($weekdays);
}
/**
* Travel the specified number of weekdays backward in time. A positive number of weekdays will travel backward in
* time, while a negative number of weekdays travels forward.
*
* @param int $weekdays The number of weekdays to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subWeekdays($weekdays) {
return $this->addWeekdays($weekdays * -1);
}
/**
* Travel a weekday, or the specified number of weekdays backward in time. A positive number of weekdays will
* travel backward in time, while a negative number of weekdays will travel forward.
*
* @param int $weekdays [optional] The number of weekdays to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subWeekday($weekdays = 1) {
return $this->subtractWeekdays($weekdays);
}
/**
* Travel the specified number of weeks forward in time. A positive number of weeks will travel forward in time,
* while a negative number of weeks travels backward.
*
* @param int $weeks The number of weeks to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addWeeks($weeks) {
// Make sure the weeks value is an integer
if(!is_int($weeks))
return null;
// Travel the specified number of weeks in time
if($this->modify($weeks . ' week') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a week, or the specified number of weeks forward in time. A positive number of weeks will travel forward
* in time, while a negative number of weeks travels backward.
*
* @param int $weeks [optional] The number of weeks to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addWeek($weeks = 1) {
return $this->addWeeks($weeks);
}
/**
* Travel the specified number of weeks backward in time. A positive number of weeks will travel backward in time,
* while a negative number of weeks travels forward.
*
* @param int $weeks The number of weeks to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subWeeks($weeks) {
return $this->addWeeks($weeks * -1);
}
/**
* Travel a week, or the specified number of weeks backward in time. A positive number of weeks will travel
* backward in time, while a negative number of weeks will travel forward.
*
* @param int $weeks [optional] The number of weeks to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subWeek($weeks = 1) {
return $this->subtractWeeks($weeks);
}
/**
* Travel the specified number of hours forward in time. A positive number of hours will travel forward in time,
* while a negative number of hours travels backward.
*
* @param int $hours The number of hours to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addHours($hours) {
// Make sure the hours value is an integer
if(!is_int($hours))
return null;
// Travel the specified number of hours in time
if($this->modify($hours . ' hour') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a hour, or the specified number of hours forward in time. A positive number of hours will travel forward
* in time, while a negative number of hours travels backward.
*
* @param int $hours [optional] The number of hours to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addHour($hours = 1) {
return $this->addHours($hours);
}
/**
* Travel the specified number of hours backward in time. A positive number of hours will travel backward in time,
* while a negative number of hours travels forward.
*
* @param int $hours The number of hours to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subHours($hours) {
return $this->addHours($hours * -1);
}
/**
* Travel a hour, or the specified number of hours backward in time. A positive number of hours will travel
* backward in time, while a negative number of hours will travel forward.
*
* @param int $hours [optional] The number of hours to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subHour($hours = 1) {
return $this->subtractHours($hours);
}
/**
* Travel the specified number of minutes forward in time. A positive number of minutes will travel forward in
* time, while a negative number of minutes travels backward.
*
* @param int $minutes The number of minutes to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addMinutes($minutes) {
// Make sure the minutes value is an integer
if(!is_int($minutes))
return null;
// Travel the specified number of minutes in time
if($this->modify($minutes . ' minute') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a minute, or the specified number of minutes forward in time. A positive number of minutes will travel
* forward in time, while a negative number of minutes travels backward.
*
* @param int $minutes [optional] The number of minutes to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addMinute($minutes = 1) {
return $this->addMinutes($minutes);
}
/**
* Travel the specified number of minutes backward in time. A positive number of minutes will travel backward in
* time, while a negative number of minutes travels forward.
*
* @param int $minutes The number of minutes to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subMinutes($minutes) {
return $this->addMinutes($minutes * -1);
}
/**
* Travel a minute, or the specified number of minutes backward in time. A positive number of minutes will travel
* backward in time, while a negative number of minutes will travel forward.
*
* @param int $minutes [optional] The number of minutes to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subMinute($minutes = 1) {
return $this->subtractMinutes($minutes);
}
/**
* Travel the specified number of seconds forward in time. A positive number of seconds will travel forward in
* time, while a negative number of seconds travels backward.
*
* @param int $seconds The number of seconds to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addSeconds($seconds) {
// Make sure the seconds value is an integer
if(!is_int($seconds))
return null;
// Travel the specified number of seconds in time
if($this->modify($seconds . ' second') === false)
return null;
// Return this for method chaining
return $this;
}
/**
* Travel a second, or the specified number of seconds forward in time. A positive number of seconds will travel
* forward in time, while a negative number of seconds travels backward.
*
* @param int $seconds [optional] The number of seconds to travel forward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function addSecond($seconds = 1) {
return $this->addSeconds($seconds);
}
/**
* Travel the specified number of seconds backward in time. A positive number of seconds will travel backward in
* time, while a negative number of seconds travels forward.
*
* @param int $seconds The number of seconds to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subSeconds($seconds) {
return $this->addSeconds($seconds * -1);
}
/**
* Travel a second, or the specified number of seconds backward in time. A positive number of seconds will travel
* backward in time, while a negative number of seconds will travel forward.
*
* @param int $seconds [optional] The number of seconds to travel backward in time.
*
* @return DateTime|null The DateTime instance for method chaining, or null on failure.
*/
public function subSecond($seconds = 1) {
return $this->subtractSeconds($seconds);
}
/**
* Get the difference between this and another date and time object.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param bool $absolute
*
* @return \DateInterval|null The difference as DateInterval or null on failure.
*/
// TODO: Return the CarbonCMS DateInterval here!
public function diff($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// TODO: Return a CarbonCMS DateInterval instance!
// Call the parent method and return the result, return null on failure
return ($diff = parent::diff($dateTime, $absolute)) === false ? null : $diff;
}
/**
* Get the difference by the given interval using a filter closure.
* The callback will be called for each period in the given time frame. If the callback returns true the period is
* included as difference, false should be returned otherwise.
*
* @param DateInterval $dateInterval An interval to traverse by.
* @param Closure $callback The callback function to call for each period as filter.
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] True to get the absolute difference, false otherwise.
*
* @return int|null The difference of the date interval in the given time frame. Null will be returned on failure.
*/
public function diffFiltered($dateInterval, Closure $callback, $dateTime = null, $absolute = true) {
// TODO: Parse the date interval!
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Define the start and end times, define whether the value should be inverted
$inverse = !$this->isLessThan($dateTime);
$start = $inverse ? $this : $dateTime;
$end = $inverse ? $dateTime : $start;
// Run the callback for all periods
$period = new DatePeriod($start, $dateInterval, $end);
$values = array_filter(iterator_to_array($period), function (DateTime $date) use ($callback) {
return call_user_func($callback, self::instance($date));
});
// Get the difference result
$diff = count($values);
// Return the difference result, inverse the value if needed
return $inverse && !$absolute ? ($diff * -1) : $diff;
}
/**
* Get the difference between this and the specified date in years.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in years.
*
* @return int|null The difference in years or null on failure.
*/
public function diffInYears($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Get the difference and make sure it's valid
if(($difference = $this->diff($dateTime, $absolute)) === false)
return null;
// Get and return the difference in years
return (int) $difference->format('%r%y');
}
/**
* Get the difference between this and the specified date in months.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in months.
*
* @return int|null The difference in months or null on failure.
*/
public function diffInMonths($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Get the differences and make sure it's valid
if(($differenceYears = $this->diffInYears($dateTime, $absolute)) === null)
return null;
if(($difference = $this->diff($dateTime, $absolute)) === false)
return null;
// Get and return the difference in months
return $differenceYears * static::MONTHS_PER_YEAR + (int) $difference->format('%r%m');
}
/**
* Get the difference between this and the specified date in weeks.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in weeks.
*
* @return int|null The difference in weeks or null on failure.
*/
public function diffInWeeks($dateTime = null, $absolute = true) {
// Get the difference in days, and make sure it's valid
if(($differenceDays = $this->diffInDays($dateTime, $absolute)) === null)
return null;
// Get and return the difference in weeks
return (int) ($differenceDays / static::DAYS_PER_WEEK);
}
/**
* Get the difference in weekdays.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
*
* @return int The difference in weekdays, or null on failure.
*/
public function diffInWeekdays($dateTime = null, $absolute = true) {
return $this->diffInDaysFiltered(function (DateTime $date) {
return $date->isWeekday();
}, $dateTime, $absolute);
}
/**
* Get the difference in weekend days.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
*
* @return int The difference in weekend days, or null on failure.
*/
public function diffInWeekendDays($dateTime = null, $absolute = true) {
return $this->diffInDaysFiltered(function (DateTime $date) {
return $date->isWeekend();
}, $dateTime, $absolute);
}
/**
* Get the difference between this and the specified date in days.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in days.
*
* @return int|null The difference in days or null on failure.
*/
public function diffInDays($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Get the difference and make sure it's valid
if(($difference = $this->diff($dateTime, $absolute)) === false)
return null;
// Get and return the difference in days
return (int) $difference->format('%r%a');
}
/**
* Get the difference in days using a filter closure
*
* @param Closure $callback The callback function to call for each day as filter.
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
*
* @return int|null The difference in days. Null will be returned on failure.
*/
public function diffInDaysFiltered(Closure $callback, $dateTime = null, $absolute = true) {
return $this->diffFiltered(DateInterval::day(), $callback, $dateTime, $absolute);
}
/**
* Get the difference between this and the specified date in hours.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in hours.
*
* @return int|null The difference in hours or null on failure.
*/
public function diffInHours($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Get the difference in seconds and make sure it's valid
if(($differenceSeconds = $this->diffInSeconds($dateTime, $absolute)) === null)
return null;
// Get and return the difference in hours
return (int) ($differenceSeconds / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
}
/**
* Get the difference in hours using a filter closure
*
* @param Closure $callback The callback function to call for each hour as filter.
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
*
* @return int|null The difference in hours. Null will be returned on failure.
*/
public function diffInHoursFiltered(Closure $callback, $dateTime = null, $absolute = true) {
return $this->diffFiltered(DateInterval::hour(), $callback, $dateTime, $absolute);
}
/**
* Get the difference between this and the specified date in minutes.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in minutes.
*
* @return int|null The difference in minutes or null on failure.
*/
public function diffInMinutes($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Get the difference in seconds, and make sure it's valid
if(($differenceSeconds = $this->diffInSeconds($dateTime, $absolute)) === null)
return null;
// Get and return the difference in minutes
return (int) ($differenceSeconds / static::SECONDS_PER_MINUTE);
}
/**
* Get the difference between this and the specified date in seconds.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in seconds.
*
* @return int|null The difference in seconds or null on failure.
*/
public function diffInSeconds($dateTime = null, $absolute = true) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Calculate the timestamp difference
$timestampDifference = $dateTime->getTimestamp() - $this->getTimestamp();
// Return the result in absolute or regular form
return $absolute ? abs($timestampDifference) : $timestampDifference;
}
/**
* Get the number of seconds since midnight.
*
* @return int The number of seconds.
*/
public function secondsSinceMidnight() {
return $this->diffInSeconds($this->copy()->startOfDay());
}
/**
* Get the number of seconds until the end of the day, which is 23:23:59.
*
* @return int The number of seconds.
*/
public function secondsUntilEndOfDay() {
return $this->diffInSeconds($this->copy()->endOfDay());
}
// TODO: Should we add the diffForHumans(); method from Carbon DateTime, and their related methods?
/**
* Set the time to the start of the day, which is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfDay() {
return $this->setHour(0)->setMinute(0)->setSecond(0);
}
/**
* Set the time to the end of the day, which is 23:59:59.
*
* @return static The DateTime instance.
*/
public function endOfDay() {
return $this->setHour(23)->setMinute(59)->setSecond(59);
}
/**
* Reset the date to the first day of the month and the time to the beginning of that day, which is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfMonth() {
return $this->setDay(1)->startOfDay();
}
/**
* Resets the date to the last day of the month and the time to the end of that day, which is 23:59:59.
*
* @return static The DateTime instance.
*/
public function endOfMonth() {
return $this->setDay($this->daysInMonth)->endOfDay();
}
/**
* Resets the date to the start of the year and the time to the beginning of that day, which is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfYear() {
return $this->setMonth(1)->startOfMonth();
}
/**
* Resets the date to the end of t he year and the time to the end of that day, which is 23:59:59.
*
* @return static The DateTime instance.
*/
public function endOfYear() {
return $this->setMonth(static::MONTHS_PER_YEAR)->endOfMonth();
}
/**
* Resets the date to the start of the decade and the time to the beginning of that day, which is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfDecade() {
return $this->startOfYear()->setYear($this->year - ($this->year % static::YEARS_PER_DECADE));
}
/**
* Resets the date to the end of the decade and the time to the end of that day, which is 23:59:59.
*
* @return static
*/
public function endOfDecade() {
return $this->endOfYear()->setYear($this->year -
($this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1));
}
/**
* Resets the date to the start of the century and the time to the beginning of that day, which is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfCentury() {
return $this->startOfYear()->setYear($this->year - ($this->year % static::YEARS_PER_CENTURY));
}
/**
* Resets the date to the end of the century and the time to the end of that day, which is 23:59:59.
*
* @return static The DateTime instance.
*/
public function endOfCentury() {
return $this->endOfYear()->setYear($this->year -
($this->year % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY - 1));
}
/**
* Resets the date to the first day of the ISO-8601 week (Monday) and the time to the beginning of that day, which
* is 00:00:00.
*
* @return static The DateTime instance.
*/
public function startOfWeek() {
// Set the date to the first day of the week
if($this->dayOfWeek != static::MONDAY)
$this->previous(static::MONDAY);
// Set the time to the start of the day
return $this->startOfDay();
}
/**
* Resets the date to the end of the ISO-8601 week (Sunday) and time the end of that day, which is 23:59:59.
*
* @return static The DateTime instance.
*/
public function endOfWeek() {
// Set the date to the last day of the week
if($this->dayOfWeek != static::SUNDAY)
$this->next(static::SUNDAY);
// Set the time to the end of the day
return $this->endOfDay();
}
/**
* Modify to the next occurrence of a given day of the week. If no specific day is provided, the next occurrence of
* the current day of the week is used. This will also reset the time to the start of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using the day constants such as static::SUNDAY. Or
* null to get the next occurrence of the current day.
*
* @return static The DateTime instance.
*/
public function next($dayOfWeek = null) {
// Use the current day of the week if none was provided
if($dayOfWeek === null)
$dayOfWeek = $this->dayOfWeek;
// Find the next occurrence of the day of the week, and return the result
return $this->modify('next ' . static::$DAY_NAMES[$dayOfWeek])->startOfDay();
}
/**
* Modify to the previous occurrence of a given day of the week. If no specific day is provided, the previous
* occurrence of the current day of the week is used. This will also reset the time to the start of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using the day constants such as static::SUNDAY. Or
* null to get the previous occurrence of the current day.
*
* @return static The DateTime instance.
*/
public function previous($dayOfWeek = null) {
// Use the current day of the week if none was provided
if($dayOfWeek === null)
$dayOfWeek = $this->dayOfWeek;
// Find the previous occurrence of the day of the week, and return the result
return $this->modify('last ' . static::$DAY_NAMES[$dayOfWeek])->startOfDay();
}
/**
* Modify to the first occurrence of a given day of the week. If no specific day is provided, the first day of the
* month is used. This will also reset the time to the start of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using the day constants such as static::SUNDAY. Or
* null to get the first day of the month.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function firstOfMonth($dayOfWeek = null) {
// Use the first day of the week if none was provided
if($dayOfWeek === null)
return $this->setDay(1)->startOfDay();
// Get the first day occurrence in the month, and make sure it's valid
if(($dateTime = $this->modify('first ' . static::$DAY_NAMES[$dayOfWeek] . ' of ' . $this->format('F') . ' ' .
$this->year)) === null
)
return null;
// Parse the date and time
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Set the time to the start of the day and return the result
return $dateTime->startOfDay();
}
/**
* Modify to the last occurrence of a given day of the week. If no specific day is provided, the last day of the
* month is used. This will also reset the time to the start of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using the day constants such as static::SUNDAY. Or
* null to get the first day of the month.
*
* @return static The DateTime instance.
*/
public function lastOfMonth($dayOfWeek = null) {
// Use the last day of the month if none was provided
if($dayOfWeek === null)
return $this->setDay($this->daysInMonth);
// Get the last day occurrence in the month
$dateTime =
$this->modify('last ' . static::$DAY_NAMES[$dayOfWeek] . ' of ' . $this->format('F') . ' ' . $this->year);
// Parse the date and time
if(($dateTime = static::parse($dateTime)) === null)
return null;
// Set the time to the start of the day and return the result
return $dateTime->startOfDay();
}
/**
* Modify to the given occurrence of a given day of the week in the current month.
* If the given day is outside the current month no modifications are made an null is returned.
* This will also reset the time to the beginning of the day.
*
* @param int $nth The occurrence of the day of the week.
* @param int $dayOfWeek The day of the week, using the day constants such as static::SUNDAY.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function nthOfMonth($nth, $dayOfWeek) {
// Get a copy of the date and time set to the first day of the month
$dateTime = $this->copy()->firstOfMonth();
// Store the year and month, to use for checking later
$check = $dateTime->format('Y-m');
// Add the number of days to the date and time
if($dateTime->modify('+' . $nth . ' ' . static::$DAY_NAMES[$dayOfWeek]) === null)
return null;
// Make sure the year and month are still the same
if($dateTime->format('Y-m') !== $check)
return null;
// Modify and return the date and time
return $this->modify($dateTime);
}
/**
* Modify to the first occurrence of a given day of the week in the current quarter. If no day of the week if
* provided, modify to the first day of the current quarter. This will also reset the time to the beginning of that
* day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using the day constants such as static::SUNDAY. Or
* null to get the first day of the current quarter.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function firstOfQuarter($dayOfWeek = null) {
return $this->setDay(1)->setMonth($this->quarter * 3 - 2)->firstOfMonth($dayOfWeek);
}
/**
* Modify to the last occurrence of the given day of the week in the current quarter. If no day of the week is
* provided, modify to the first day of the current quarter. This will also reset the time to the beginning of that
* day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using day constants such as static::SUNDAY. Or null
* to get the last day of the current quarter.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function lastOfQuarter($dayOfWeek = null) {
return $this->setDay(1)->setMonth($this->quarter * 3)->lastOfMonth($dayOfWeek);
}
/**
* Modify to the given occurrence of a given day of the week in the current quarter.
* If the given day is outside the current quarter no modifications are made an null is returned.
* This will also reset the time to the beginning of that day.
*
* @param int $nth The occurrence of the day of the week.
* @param int $dayOfWeek The day of the week, using the day constants such as static::SUNDAY.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function nthOfQuarter($nth, $dayOfWeek) {
// Get a copy of the date and time set to the first day of the month
$dateTime = $this->copy()->setDay(1)->setMonth($this->quarter * 3);
// Get the last month and the year of the quarter
$last_month = $dateTime->getMonth();
$year = $dateTime->getYear();
// Get the nth occurrence of the day of the week in this quarter
// TODO: Why is the modify method not recognized?
$dateTime->firstOfQuarter()->modify('+' . $nth . ' ' . static::$DAY_NAMES[$dayOfWeek]);
// Make sure the date is not outside the current quarter
if($last_month < $dateTime->getMonth() || $year !== $dateTime->getYear())
return null;
// Modify the date and time, return the result
return $this->modify($dateTime);
}
/**
* Modify to the first occurrence of a given day of the week in the current year. If no day of the week is provided
* the first day of the year is used. This will also reset the time to the beginning of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using day constants such as static::SUNDAY. Or null
* to get the first day of the current year.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function firstOfYear($dayOfWeek = null) {
return $this->setMonth(1)->firstOfMonth($dayOfWeek);
}
/**
* Modify to the last occurrence of a given day of the week in the current year. If no day of the week is provided
* the last day of the year is used. This will also reset the time to the beginning of that day.
*
* @param int|null $dayOfWeek [optional] The day of the week, using day constants such as static::SUNDAY. Or null
* to get the last day of the current year.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function lastOfYear($dayOfWeek = null) {
return $this->setMonth(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek);
}
/**
* Modify to the given occurrence of a given day of the week in the current year.
* If the given day is outside the current year no modifications are made an null is returned.
* This will also reset the time to the beginning of that day.
*
* @param int $nth The occurrence of the day of the week.
* @param int $dayOfWeek The day of the week, using the day constants such as static::SUNDAY.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function nthOfYear($nth, $dayOfWeek) {
// Create a copy of the date and time, and get the nth day of the week
$dateTime = $this->copy()->firstOfYear()->modify('+' . $nth . ' ' . static::$DAY_NAMES[$dayOfWeek]);
// Make sure the date isn't outside the current year
if($this->year != $dateTime->year)
return null;
// Modify and return the date and time
return $this->modify($dateTime);
}
/**
* Get the age of the date and time compared to the current date and time specified by the now() method.
*
* @return int The age of the date and time.
*/
public function getAge() {
return $this->diffInYears(null, false);
}
/**
* Check if it's the birthday. This check whether the month and day are equal to the specified date and time.
*
* @param DateTime|PHPDateTime|string|null $birthday [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
*
* @return boolean True if it's the birthday, false otherwise. False is also returned on failure.
*/
public function isBirthday($birthday) {
// Parse the date and time, return null on failure
if(($birthday = static::parse($birthday, $this->getTimezone())) === null)
return null;
// Check whether the month and day are equal, return the result
return StringUtils::equals($this->format('md'), $birthday->format('md'));
}
/**
* Modify the date and time to the average of the date and time and the specified date and time.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
*
* @return static|null The DateTime instance, or null on failure.
*/
public function average($dateTime = null) {
// Parse the date and time, return null on failure
if(($dateTime = static::parse($dateTime, $this->getTimezone())) === null)
return null;
// Calculate the difference in seconds and make sure it's valid
if(($differenceSeconds = $this->diffInSeconds($dateTime, false)) === null)
return null;
// Apply the average difference and return the result
return $this->addSeconds((int) ($differenceSeconds / 2));
}
/**
* Get the date and time as a string formatted according to given format.
*
* @param string|null $format [optional] The desired format for the date and time, or null to use the default
* format.
*
* @return string|null The date and time as a string, or null on failure.
*/
public function format($format = null) {
// Use the default format if the format parameter is null
if($format === null)
$format = self::DEFAULT_FORMAT;
// Get and return the date and time with the proper format, return null on failure
return ($result = parent::format($format)) === false ? null : $result;
}
/**
* Format the date and time as a string.
*
* @param string|null $format [optional] The format to return the date and time with as a string.
*
* @return string|null The date and time as a string, or null on failure.
*/
// TODO: Should we use some kind of human readable formatting, or should we create a different function for this?
public function toString($format = null) {
// Use the default format if it's set to null
if($format === null)
$format = static::DEFAULT_FORMAT;
// Get the date and time as a string with the proper format and return the result, return null on failure
return ($result = $this->format($format)) === false ? null : $result;
}
/**
* Format the date and time as a string.
*
* @return string The date and time as a string, with the default format.
*/
// TODO: To string for humans!
public function __toString() {
// Get the date and time as a string, return an empty string on failure
return ($result = $this->toString()) === null ? '' : $result;
}
/**
* Format the date as a string.
*
* @return string The date as a string.
*/
public function toDateString() {
return $this->format(static::DEFAULT_FORMAT_DATE);
}
/**
* Format the time as a string.
*
* @return string The time as a string.
*/
public function toTimeString() {
return $this->format(static::DEFAULT_FORMAT_TIME);
}
/**
* Convert the date and time into a string with complete formatting.
*
* @return string|null The date and time as a string, or null on failure.
*/
// TODO: Should we create a different method for this, or rename this method?
public function toCompleteString() {
return $this->toString(static::DEFAULT_FORMAT_COMPLETE);
}
}
<?php
/**
* DateTimeUtils.php
*
* A utilities class for the DateTime class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Carbon CMS 2015. All rights reserved.
*/
namespace carbon\core\datetime;
use carbon\core\datetime\interval\DateInterval;
use carbon\core\datetime\zone\DateTimeZone;
use Closure;
use DateTime as PHPDateTime;
use DateTimeZone as PHPDateTimeZone;
use InvalidArgumentException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* A utilities class for the DateTime class.
*
* @package carbon\core\datetime
*/
class DateTimeUtils {
// TODO: Make sure all DateTime and PHPDateTime instances are correct!
/**
* Parse a date and time with an optional time zone. A new instance will be created if required.
*
* If the $dateTime parameter is a DateTime zone instance, the instance will be returned and the $timezone
* parameter is ignored. If the $dateTime parameter is anything other than a DateTime zone the date, time and the
* time zone is parsed through the constructor.
*
* This method allows better fluent syntax because it makes method chaining possible.
*
* @param DateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the time as a string, or
* null to use the now() time.
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone [optional] The time zone the
* specified time is in, or null to use the default time zone if the $time param isn't a DateTime instance. A
* DateTime or PHPDateTime instance to use it's timezone.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateTime|mixed The DateTime instance, or the default value on failure.
*
* @TODO Is this exception indeed thrown, or is it being catched?
* @throws InvalidArgumentException Throws an exception if the date and time couldn't be parsed.
*/
// TODO: Don't use exception catching!
public static function parse($dateTime = null, $timezone = null, $default = null) {
// Return the object if it's already a DateTime instance
if($dateTime instanceof DateTime)
return $dateTime;
// Parse and return the date and time, return the default value on failure
try {
return new DateTime($dateTime, $timezone);
} catch(InvalidArgumentException $ex) {
return $default;
}
}
/**
* Check whether the date and time specified by a equals b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date and time of a and b equals, false if not. The default value will be returned
* on failure.
*/
public static function equals($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the result, return the default value on failure
return ($result = $a->equals($b)) === null ? $default : $result;
}
/**
* Check whether the date and time specified by a is greater than b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date and time of a is greater than b, false if not. The default value will be
* returned on failure.
*/
// TODO: Does this work, or should we compare the timestamps?
public static function isGreaterThan($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the result, return the default value on failure
return ($result = $a->isGreaterThan($b)) === null ? $default : $result;
}
/**
* Check whether the date and time specified by a is greater or equal to b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date and time of a is greater or equal to b, false if not. The default value will
* be returned on failure.
*/
// TODO: Does this work, or should we compare the timestamps?
// TODO: Should we rename this to isGreaterThanOrEqualTo
public static function isGreaterOrEqualTo($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the result, return the default value on failure
return ($result = $a->isGreaterOrEqualTo($b)) === null ? $default : $result;
}
/**
* Check whether the date and time specified by a is less than b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date and time of a is less than b, false if not. The default value will be
* returned on failure.
*/
// TODO: Does this work, or should we compare the timestamps?
public static function isLessThan($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the result, return the default value on failure
return ($result = $a->isLessThan($b)) === null ? $default : $result;
}
/**
* Check whether the date and time specified by a is less or equal to b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date and time of a is less or equal b, false if not. The default value will be
* returned on failure.
*/
// TODO: Does this work, or should we compare the timestamps?
// TODO: Should we rename this to isLessThanOrEqualTo?
public static function isLessOrEqualTo($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the result, return the default value on failure
return ($result = $a->isLessOrEqualTo($b)) === null ? $default : $result;
}
/**
* Check whether the specified date and time is between a and b.
* The $a and $b parameter may not be null at the same time or false will be returned.
*
* @param DateTime|PHPDateTime|string|null $dateTime The date and time that needs to be in between a and be as
* DateTime or PHPDateTime instance, the date and time as a string or null to use the now() value.
* @param DateTime|PHPDateTime|string|null $a The date and time as DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b The date and time as DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param bool $equals [optional] True to also return true if the date equals one of the specified date and times,
* false otherwise.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date is between the specified date and time, or if it equals one of the date and
* times while $equals is set to true, false if not. The default value will be returned on failure.
*/
public static function isBetween($dateTime = null, $a = null, $b = null, $equals = true, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the date and time is in between and return the result, return the default value on failure
return ($result = $dateTime->isBetween($a, $b, $equals)) === null ? $default : $result;
}
/**
* Get the greatest date and time of a and b. If both are equal, a will be returned.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateTime|mixed The greatest DateTime instance, or the default value on failure.
*/
public static function max($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the greatest, return the default value on failure
return ($result = $a->max($b)) === null ? $default : $result;
}
/**
* Get the lowest date and time of a and b. If both are equal, a will be returned.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateTime|mixed The lowest DateTime instance, or the default value on failure.
*/
public static function min($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Compare a and b and return the smallest, return the default value on failure
return ($result = $a->min($b)) === null ? $default : $result;
}
/**
* Check whether the specified date is a weekday (monday to friday).
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if this is a weekday, false if not. The default value will be returned on failure.
*/
public static function isWeekday($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is a weekday, return the result
return $dateTime->isWeekday();
}
/**
* Check whether the specified date is a weekend day (saturday or sunday).
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if this is a weekend day, false if not. The default value on failure.
*/
public static function isWeekend($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is in the weekend, return the result
return $dateTime->isWeekend();
}
/**
* Check whether the specified date is today.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if this is today, false if not. The default value will be returned on failure.
*/
public static function isToday($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is today, return the result
return $dateTime->isToday();
}
/**
* Check whether the specified date is tomorrow.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if this is tomorrow, false if not. The default value will be returned on failure.
*/
public static function isTomorrow($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is tomorrow, return the result
return $dateTime->isTomorrow();
}
/**
* Check whether the specified date is yesterday.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool true if this is yesterday, false if not. The default value will be returned on failure.
*/
public static function isYesterday($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is yesterday, return the result
return $dateTime->isYesterday();
}
/**
* Check whether the specified date and time is in the future. If the date and time equals the now() date and time
* false is returned.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if this is in the future, false if not. The default value will be returned on failure.
*/
public static function isFuture($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is in the future, return the result
return $dateTime->isFuture();
}
/**
* Check whether the specified date and time is in the past. If the date and time equals the now() date and time
* false is returned.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if this is in the past, false if not. The default value will be returned on failure.
*/
public static function isPast($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is in the past, return the result
return $dateTime->isPast();
}
/**
* Check whether the specified date is a leap year.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if this is a leap year, false if not. The default value will be returned on failure.
*/
public static function isLeapYear($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether the specified date is a leap year, return the result
return $dateTime->isLeapYear();
}
/**
* Check whether the year of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if year is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameYear($a = null, $b = null, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameYear($b);
}
/**
* Check whether the month of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if month is the same as the specified date, false otherwise. False will also be returned on
* failure.
*/
public function isSameMonth($a = null, $b = null, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameMonth($b);
}
/**
* Check whether the ISO-8601 week of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if the ISO-8601 week is the same as the specified date, false otherwise. False will also be
* returned on failure.
*/
public function isSameWeek($a = null, $b = null, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameWeek($b);
}
/**
* Check whether the day of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the date of a and b are the same, false if not. The default value will be returned on
* failure.
*/
public static function isSameDate($a = null, $b = null, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameDate($b);
}
/**
* Check whether the hour of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if hour is the same as the specified date and time, false otherwise. False will also be
* returned on failure.
*/
public function isSameHour($a = null, $b = null, $checkDate = true, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameHour($b, $checkDate);
}
/**
* Check whether the minute of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if minute is the same as the specified date and time, false otherwise. False will also be
* returned on failure.
*/
public function isSameMinute($a = null, $b = null, $checkDate = true, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameMinute($b, $checkDate);
}
/**
* Check whether the time of a and b is the same.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() time.
* @param bool $checkDate [optional] True to make sure the dates are equal, false to just compare the time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if time is the same as the specified date and time, false otherwise. False will also be
* returned on failure.
*/
public function isSameTime($a = null, $b = null, $checkDate = true, $default = null) {
// Parse the date and time of a and b, return the default value on failure
if(($a = static::parse($a)) === null || ($b = static::parse($b)) === null)
return $default;
// Check whether the time of a and b is the same and return the result
return $a->isSameTime($b, $checkDate);
}
/**
* Get the difference of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in years.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return \DateInterval|mixed The difference of a and b, or the default value on failure.
*/
public static function diff($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// TODO: Return a Carbon CMS DateInterval instance!
// Get the difference of a and b and return the result, return the default value on failure
return ($result = $a->diff($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference of a and b by the given interval using a filter closure.
* The callback will be called for each period in the given time frame. If the callback returns true the period is
* included as difference, false should be returned otherwise.
*
* @param DateInterval $dateInterval An interval to traverse by.
* @param Closure $callback The callback function to call for each period as filter.
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() date and time.
* @param boolean $absolute [optional] True to get the absolute difference, false otherwise.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference of the date interval in the given time frame. The default value will be
* returned on failure.
*/
public function diffFiltered($dateInterval, Closure $callback, $a = null, $b = null, $absolute = true,
$default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Call the difference filtered method on a and return the result, return the default value on failure
return ($result = $a->diffFiltered($dateInterval, $callback, $b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in years of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in years.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInYears($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in years of a and b and return the result, return the default value on failure
return ($result = $a->diffInYears($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in months of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in months.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInMonths($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in months of a and b and return the result, return the default value on failure
return ($result = $a->diffInMonths($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in weeks of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in weeks.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInWeeks($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in weeks of a and b and return the result, return the default value on failure
return ($result = $a->diffInWeeks($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in weekdays of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int The difference in weekdays, or the default value returned on failure.
*/
public function diffInWeekdays($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in weekdays of a and b and return the result, return the default value on failure
return ($result = $a->diffInWeekdays($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in weekend days of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int The difference in weekend days, or the default value returned on failure.
*/
public function diffInWeekendDays($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in weekend days of a and b and return the result, return the default value on failure
return ($result = $a->diffInWeekendDays($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in days of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in days.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInDays($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in days of a and b and return the result, return the default value on failure
return ($result = $a->diffInDays($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in days of a and b using a filter closure.
*
* @param Closure $callback The callback function to call for each day as filter.
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|null The difference in days. The default value will be returned on failure.
*/
public function diffInDaysFiltered(Closure $callback, $a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in days of a and b using a filter closure and return the result, return the default value on failure
return ($result = $a->diffInDaysFiltered($callback, $b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in hours of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in hours.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInHours($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in hours of a and b and return the result, return the default value on failure
return ($result = $a->diffInHours($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in hours of a and b using a filter closure.
*
* @param Closure $callback The callback function to call for each hour as filter.
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute Get the absolute of the difference.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|null The difference in hours. The default value will be returned on failure.
*/
public function diffInHoursFiltered(Closure $callback, $a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in hours of a and b using a filter closure and return the result, return the default value on failure
return ($result = $a->diffInHoursFiltered($callback, $b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in minutes of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in minutes.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInMinutes($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in minutes of a and b and return the result, return the default value on failure
return ($result = $a->diffInMinutes($b, $absolute)) === null ? $default : $result;
}
/**
* Get the difference in seconds of a and b.
*
* @param DateTime|PHPDateTime|string|null $a [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] A DateTime or PHPDateTime instance, the date and time as a
* string or null to use the now() date and time.
* @param boolean $absolute [optional] Get the absolute of the difference in seconds.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The difference in years of a and b, or the default value on failure.
*/
public static function diffInSeconds($a = null, $b = null, $absolute = true, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the difference in seconds of a and b and return the result, return the default value on failure
return ($result = $a->diffInSeconds($b, $absolute)) === null ? $default : $result;
}
/**
* Get the number of seconds since midnight of the specified date and time.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as string or null to use the now() date and time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The number of seconds or the default value on failure.
*/
public static function secondsSinceMidnight($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Get and return the number of seconds since midnight
return $dateTime->secondsSinceMidnight();
}
/**
* Get the number of seconds of the specified date and time until the end of the day, which is 23:23:59.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] A DateTime or PHPDateTime instance, the date and
* time as string or null to use the now() date and time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The number of seconds until the end of the day, or the default value on failure.
*/
public static function secondsUntilEndOfDay($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Get and return the number of seconds since midnight
return $dateTime->secondsUntilEndOfDay();
}
/**
* Get the age of the specified date and time compared to the current date and time specified by the now() method.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|mixed The age of the date and time, or the default value on failure.
*/
public function getAge($dateTime = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Get the age, return the default value on failure
if(($age = $dateTime->getAge()) === null)
return $default;
// Return the age
return $age;
}
/**
* Check whether it's the birthday of the specified date and time. This compares the month and day of both dates.
*
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $birthday [optional] The DateTime or PHPDateTime instance, the date and
* time as a string or null to use the now() date and time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return boolean True if it's the birthday, false if not. The default value will be returned on failure.
*/
public static function isBirthday($dateTime = null, $birthday = null, $default = null) {
// Parse the date and time, return the default value on failure
if(($dateTime = static::parse($dateTime)) === null)
return $default;
// Check whether it's the birthday of the specified date and time and return the result, return the default value on failure
return ($result = $dateTime->isBirthday($birthday)) === null ? $default : $result;
}
/**
* Get the average in date and time of a and b. A new DateTime instance is returned with the average date and time.
*
* @param DateTime|PHPDateTime|string|null $a [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() date and time.
* @param DateTime|PHPDateTime|string|null $b [optional] The DateTime or PHPDateTime instance, the date and time as
* a string or null to use the now() date and time.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateTime|mixed The average as DateTime instance, or the default value on failure.
*/
public static function average($a = null, $b = null, $default = null) {
// Parse the date and time of a, return the default value on failure
if(($a = static::parse($a)) === null)
return $default;
// Get the average date and time of a and b and return the result, return the default value on failure
return ($result = $a->copy()->average($b)) === null ? $default : $result;
}
}
<?php
namespace carbon\core\datetime\interval;
use carbon\core\datetime\DateTime;
use carbon\core\datetime\interval\spec\DateIntervalSpecUtils;
use DateInterval as PHPDateInterval;
use DomainException;
use Exception;
use InvalidArgumentException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Class DateInterval
*
* @package carbon\core\datetime
*
* @TODO Update these properties below!
* @TODO Rename this to 'daysOfWeek'?
*
* property-read int $daysExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
*/
class DateInterval extends PHPDateInterval {
// TODO: Should we create a DateDuration class which is similar to this class?
// TODO: Throw better exceptions!
// TODO: Create array utils!
/**
* The date interval specification prefix designator.
*
* @const string The period prefix.
*/
const PERIOD_PREFIX = 'P';
/**
* The date interval specification year designator.
*
* @const string The year prefix.
*/
const PERIOD_YEARS = 'Y';
/**
* The date interval specification month designator.
*
* @const string The month prefix.
*/
const PERIOD_MONTHS = 'M';
/**
* The date interval specification day designator.
*
* @const string The day prefix.
*/
const PERIOD_DAYS = 'D';
/**
* The date interval specification time designator.
*
* @const string The time prefix.
*/
const PERIOD_TIME_PREFIX = 'T';
/**
* The date interval specification hour designator.
*
* @const string The hour prefix.
*/
const PERIOD_HOURS = 'H';
/**
* The date interval specification minute designator.
*
* @const string The minute prefix.
*/
const PERIOD_MINUTES = 'M';
/**
* The date interval specification second designator.
*
* @const string The second prefix.
*/
const PERIOD_SECONDS = 'S';
/**
* Constructor.
*
* @param string|PHPDateInterval|PHPDateInterval|null $dateIntervalSpec [optional] A date interval specification, a
* relative date and time string, a DateInterval or PHPDateInterval instance, or null to use a zero specification.
* @param bool $inverted [optional] Defines whether the date interval specification is inverted or not, this
* parameter is ignored if a DateInterval or PHPDateInterval instance is given for the $dateIntervalSpec
* parameter.
*
* @throws Exception Throws an exception on failure.
*/
public function __construct($dateIntervalSpec, $inverted = false) {
// Try to parse the date interval specification
if(($spec = DateIntervalSpecUtils::parse($dateIntervalSpec, null)) === null)
throw new Exception('Invalid date interval specification (\'' . $dateIntervalSpec . '\' was given)');
// Copy the inverted state from DateInterval objects
if($dateIntervalSpec instanceof parent)
$inverted = $dateIntervalSpec->invert;
// Construct the parent object, and set whether the date interval is inverted
parent::__construct($spec);
$this->setInverted($inverted);
}
/**
* Parse a date interval. A new instance may be created.
*
* This method allows better fluent syntax because it makes method chaining possible.
*
* @param PHPDateInterval|PHPDateInterval|string|null $dateInterval [optional] A DateInterval or PHPDateInterval
* instance, a date interval specification, or null to use a zero specification.
*
* @return static A DateInterval instance, or null on failure.
*/
public static function parse($dateInterval) {
// Return the object if it's already a DateInterval instance
if($dateInterval instanceof self)
return $dateInterval;
// Return a zero specification if the specification is set to null
if($dateInterval === null)
return DateIntervalFactory::zero();
// Parse DateInterval objects
if($dateInterval instanceof DateInterval)
return $dateInterval;
// Parse PHPDateInterval objects
if($dateInterval instanceof PHPDateInterval)
return new static($dateInterval, $dateInterval->invert);
// Try to parse the string as date interval specification, return null on failure
if(($dateIntervalSpec = DateIntervalSpecUtils::parse($dateInterval, null)) !== null)
// Create and return a new date interval instance based on the parsed specification
return new static($dateIntervalSpec);
// Couldn't parse the date interval instance, return null
return null;
}
/**
* Create a DateInterval instance based on the relative parts of the string.
*
* @param string $dateInterval The string with the relative parts for the date interval.
*
* @return DateInterval The DateInterval instance, may be zero if the string doesn't have any relative parts.
*
* @throw DomainException Throws an exception if the date interval specification isn't a string.
*/
// TODO: Make a static version of this method in the utilities class!
// TODO: Catch errors caused by negative values!
public static function createFromDateString($dateInterval) {
// Make sure the date interval parameter is a string, throw an exception if this is not the case
if(!is_string($dateInterval)) {
/** @noinspection PhpParamsInspection */
$type = is_object($dateInterval) ? get_class($dateInterval) : gettype($dateInterval);
throw new DomainException('The date interval specification must be a string (' . $type . ' given)');
}
// Create and return a DateInterval instance
return self::parse(parent::createFromDateString($dateInterval));
}
/**
* Create a DateInterval instance from a DateInterval one. Can not instance
* DateInterval objects created from DateTime::diff() as you can't externally
* set the $days field.
*
* @param PHPDateInterval|PHPDateInterval|string $dateInterval
*
* @throws InvalidArgumentException
*
* @return static A new DateTime instance.
*/
// TODO: Update docs!
// TODO: Parse the date interval parameter?
// TODO: Support null!
public static function instance($dateInterval) {
// TODO: Is this necessary?
if(DateIntervalUtils::isCreatedFromDiff($dateInterval))
throw new InvalidArgumentException("Can not instance a DateInterval object created from DateTime::diff().");
// Parse DateInterval instances, create a new instance
if($dateInterval instanceof self)
return new static($dateInterval->toSpecString(), $dateInterval->isInverted());
// Try to parse and instantiate other specifications and return the result
return static::parse($dateInterval);
}
/**
* Create a copy of this instance.
*
* @return PHPDateInterval The DateInterval instance copy.
*/
public function copy() {
return static::instance($this);
}
/**
* Clone this instance.
*
* @return PHPDateInterval A DateInterval instance clone.
*/
public function __clone() {
return $this->copy();
}
/**
* Add the given number of years, months, weeks, days, hours, minutes and seconds to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $years The number of years to add to the interval. Null to ignore this value.
* @param int|null $months The number of months to add to the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks to add to the interval, this is converted into and added to the
* number of days. Null to ignore this value.
* @param int|null $days The number of days to add to the interval. Null to ignore this value.
* @param int|null $hours The number of hours to add to the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes to add to the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds to add to the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addDateTime($years = null, $months = null, $weeks = null, $days = null, $hours = null,
$minutes = null, $seconds = null) {
// Add the date and time part
$this->addDate($years, $months, $weeks, $days);
$this->addTime($hours, $minutes, $seconds);
// Return this instance
return $this;
}
/**
* Subtract the given number of years, months, weeks, days, hours, minutes and seconds to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $years The number of years to subtract to the interval. Null to ignore this value.
* @param int|null $months The number of months to subtract to the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks to subtract to the interval, this is converted into and added to the
* number of days. Null to ignore this value.
* @param int|null $days The number of days to subtract to the interval. Null to ignore this value.
* @param int|null $hours The number of hours to subtract to the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes to subtract to the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds to subtract to the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subDateTime($years = null, $months = null, $weeks = null, $days = null, $hours = null,
$minutes = null, $seconds = null) {
// Subtract the date and time part
$this->subDate($years, $months, $weeks, $days);
$this->subTime($hours, $minutes, $seconds);
// Return this instance
return $this;
}
/**
* Set the time part of the interval. This changes the specified hours, minutes and seconds.
*
* @param int|null $years The number of years of the interval. Null to ignore this value.
* @param int|null $months The number of months of the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks of the interval, this is converted into and added to the number of
* days. Null to ignore this value.
* @param int|null $days The number of days of the interval. Null to ignore this value.
* @param int|null $hours The number of hours of the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes of the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds of the interval. Null to ignore this value.
*
* @return static This DateInterval instance for method chaining.
*/
public function setDateTime($years = null, $months = null, $weeks = null, $days = null, $hours = null,
$minutes = null, $seconds = null) {
// Set the date and time part
$this->setDate($years, $months, $weeks, $days);
$this->setTime($hours, $minutes, $seconds);
// Return this instance
return $this;
}
/**
* Add the given number of years, months, weeks and days to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $years The number of years to add to the interval. Null to ignore this value.
* @param int|null $months The number of months to add to the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks to add to the interval, this is converted into and added to the
* number of days. Null to ignore this value.
* @param int|null $days The number of days to add to the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addDate($years = null, $months = null, $weeks = null, $days = null) {
// Add the number of years and months if set
if(!empty($years))
$this->addYears($years);
if(!empty($months))
$this->addMonths($months);
// Add the number of weeks and days
$this->addWeeksAndDays($weeks, $days);
// Return this instance for method chaining
return $this;
}
/**
* Subtract the given number of years, months, weeks and days to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $years The number of years to subtract of the interval. Null to ignore this value.
* @param int|null $months The number of months to subtract of the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks to subtract of the interval, this is converted into and added to the
* number of days. Null to ignore this value.
* @param int|null $days The number of days to subtract of the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subDate($years = null, $months = null, $weeks = null, $days = null) {
// Subtract the number of years and months if set
if(!empty($years))
$this->subYears($years);
if(!empty($months))
$this->subMonths($months);
// Subtract the number of weeks and days
$this->subWeeksAndDays($weeks, $days);
// Return this instance for method chaining
return $this;
}
/**
* Set the date part of the interval. This changes the specified years, months and days.
*
* @param int|null $years The number of years of the interval. Null to ignore this value.
* @param int|null $months The number of months of the interval. Null to ignore this value.
* @param int|null $weeks The number of weeks of the interval, this is converted into and added to the number of
* days. Null to ignore this value.
* @param int|null $days The number of days of the interval. Null to ignore this value.
*
* @return static This DateInterval instance for method chaining.
*/
public function setDate($years = null, $months = null, $weeks = null, $days = null) {
// Set the years and months if set
if(!empty($years))
$this->setYears($years);
if(!empty($months))
$this->setMonths($months);
// Set the days
$this->setWeeksAndDays($weeks, $days);
// Return this instance
return $this;
}
/**
* Get the number of years.
*
* @return int Number of years.
*/
public function getYears() {
return $this->y;
}
/**
* Add the given number of years to the date interval.
* The resulting number of years may not be a negative number, or an exception will be thrown.
*
* @param int $years [optional] The number of years to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addYears($years = 1) {
// Make sure the years parameter is an integer
if(!is_int($years))
throw new DomainException('Invalid value for years, must be an integer (\'' . $years . '\' was given)');
// Calculate the new number of years
$years = $this->getYears() + intval($years);
// Make sure the number isn't negative
if($years < 0)
throw new Exception('The resulting number of years may not be negative (' . $years . ' is negative)');
// Set the number of years
$this->setYears($years);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of years to the date interval.
* The resulting number of years may not be a negative number, or an exception will be thrown.
*
* @param int $years [optional] The number of years to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addYear($years = 1) {
return $this->addYears($years);
}
/**
* Subtract the given number of years to the date interval.
* The resulting number of years may not be a negative number, or an exception will be thrown.
*
* @param int $years [optional] The number of years to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subYears($years = 1) {
// Make sure the years parameter is an integer
if(!is_int($years))
throw new \DomainException('Invalid value for years, must be an integer (\'' . $years . '\' was given)');
// Subtract the number of years, return the result
return $this->addYears($years * -1);
}
/**
* Subtract one, or the given number of years to the date interval.
* The resulting number of years may not be a negative number, or an exception will be thrown.
*
* @param int $years [optional] The number of years to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subYear($years = 1) {
return $this->subYears($years);
}
/**
* Set the number of years.
*
* @param int $years The number of years, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setYears($years) {
// Make sure the value is a positive number or zero
if(!is_int($years) || $years < 0)
throw new \DomainException('Invalid value for years, must be zero or a positive number (\'' . $years .
'\' was given)');
// Set the years, return this instance
$this->y = $years;
return $this;
}
/**
* Get the number of months.
*
* @return int Number of months.
*/
public function getMonths() {
return $this->m;
}
/**
* Add the given number of months to the date interval.
* The resulting number of months may not be a negative number, or an exception will be thrown.
*
* @param int $months [optional] The number of months to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addMonths($months = 1) {
// Make sure the months parameter is an integer
if(!is_int($months))
throw new DomainException('Invalid value for months, must be an integer (\'' . $months . '\' was given)');
// Calculate the new number of months
$months = $this->getMonths() + intval($months);
// Make sure the number isn't negative
if($months < 0)
throw new Exception('The resulting number of months may not be negative (' . $months . ' is negative)');
// Set the number of months
$this->setMonths($months);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of months to the date interval.
* The resulting number of months may not be a negative number, or an exception will be thrown.
*
* @param int $months [optional] The number of months to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addMonth($months = 1) {
return $this->addMonths($months);
}
/**
* Subtract the given number of months to the date interval.
* The resulting number of months may not be a negative number, or an exception will be thrown.
*
* @param int $months [optional] The number of months to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subMonths($months = 1) {
// Make sure the months parameter is an integer
if(!is_int($months))
throw new DomainException('Invalid value for months, must be an integer (\'' . $months . '\' was given)');
// Subtract the number of months, return the result
return $this->addMonths($months * -1);
}
/**
* Subtract one, or the given number of months to the date interval.
* The resulting number of months may not be a negative number, or an exception will be thrown.
*
* @param int $months [optional] The number of months to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subMonth($months = 1) {
return $this->subMonths($months);
}
/**
* Set the number of months.
*
* @param int $months The number of months, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setMonths($months) {
// Make sure the value is a positive number or zero
if(!is_int($months) || $months < 0)
throw new DomainException('Invalid value for months, must be zero or a positive number (\'' . $months .
'\' was given)');
// Set the months, return this instance
$this->m = $months;
return $this;
}
/**
* Add the given number of weeks and days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int|null $weeks [optional] The number of weeks to add. Null to ignore this value.
* @param int|null $days [optional] The number of days to add. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addWeeksAndDays($weeks = null, $days = null) {
// Calculate the total number of days
$totalDays = 0;
// Append the number of weeks if set
if(is_int($weeks))
$totalDays += intval($weeks) * DateTime::DAYS_PER_WEEK;
else if(!empty($weeks))
throw new DomainException('Invalid value for weeks (\'' . $weeks . '\' was given)');
// Append the number of days if set
if(is_int($days))
$totalDays += intval($days);
else if(!empty($days))
throw new DomainException('Invalid value for days (\'' . $days . '\' was given)');
// Set the number of days
if(!empty($totalDays)) {
// Make sure the number of days isn't negative
if(($this->getDays() + $totalDays) < 0)
throw new DomainException('The resulting number of days may not be below zero (got ' . $totalDays .
' days)');
// Add the number of days
$this->addDays($totalDays);
}
// Return this instance
return $this;
}
/**
* Subtract the given number of weeks and days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int|null $weeks [optional] The number of weeks to subtract. Null to ignore this value.
* @param int|null $days [optional] The number of days to subtract. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subWeeksAndDays($weeks = null, $days = null) {
// Calculate the total number of days
$totalDays = 0;
// Append the number of weeks if set
if(is_int($weeks))
$totalDays += intval($weeks) * DateTime::DAYS_PER_WEEK;
else if(!empty($weeks))
throw new DomainException('Invalid value for weeks (\'' . $weeks . '\' was given)');
// Append the number of days if set
if(is_int($days))
$totalDays += intval($days);
else if(!empty($days))
throw new DomainException('Invalid value for days (\'' . $days . '\' was given)');
// Set the number of days
if(!empty($totalDays)) {
// Make sure the number of days isn't negative
if(($this->getDays() + $totalDays) < 0)
throw new DomainException('The resulting number of days may not be below zero (got ' . $totalDays .
' days)');
// Subtract the number of days
$this->subDays($totalDays);
}
// Return this instance
return $this;
}
/**
* Set the number of days based ont he given number of weeks and days.
*
* @param int|null $weeks The number of weeks of the interval, this is converted into and added to the number of
* days. Null to ignore this value.
* @param int|null $days The number of days of the interval. Null to ignore this value.
*
* @return static This DateTime instance for method chaining.
*/
public function setWeeksAndDays($weeks = null, $days = null) {
// Calculate the total number of days
$totalDays = 0;
// Append the number of weeks if set
if(is_int($weeks))
$totalDays += intval($weeks) * DateTime::DAYS_PER_WEEK;
else if(!empty($weeks))
throw new DomainException('Invalid value for weeks (\'' . $weeks . '\' was given)');
// Append the number of days if set
if(is_int($days))
$totalDays += intval($days);
else if(!empty($days))
throw new DomainException('Invalid value for days (\'' . $days . '\' was given)');
// Set the number of days
if(!empty($totalDays)) {
// Make sure the number of days isn't negative
if($totalDays < 0)
throw new DomainException('The total number of days may not be below zero (' . $totalDays .
' total days)');
// Set the number of days
$this->setDays($totalDays);
}
// Return this instance
return $this;
}
/**
* Get the approximate number of weeks. This value is based on the number of days specified, and thus isn't exact.
*
* @return int The approximate number of weeks.
*/
public function getApproximateWeeks() {
return (int) ($this->getDays() / DateTime::DAYS_PER_WEEK);
}
/**
* Add the given number of days to the date interval specified as weeks.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $weeks [optional] The number of weeks to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addWeeks($weeks = 1) {
// Make sure the weeks parameter is an integer
if(!is_int($weeks))
throw new DomainException('Invalid value for weeks, must be an integer (\'' . $weeks . '\' was given)');
// Calculate the new number of days
$days = $this->getDays() + (intval($weeks) * DateTime::DAYS_PER_WEEK);
// Make sure the number isn't negative
if($days < 0)
throw new Exception('The resulting number of days may not be negative (' . $days . ' is negative)');
// Set the number of days
$this->setDays($weeks);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of days to the date interval specified as weeks.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $weeks [optional] The number of weeks to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addWeek($weeks = 1) {
return $this->addWeeks($weeks);
}
/**
* Subtract the given number of days to the date interval specified as weeks.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $weeks [optional] The number of weeks to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subWeeks($weeks = 1) {
// Make sure the weeks parameter is an integer
if(!is_int($weeks))
throw new DomainException('Invalid value for weeks, must be an integer (\'' . $weeks . '\' was given)');
// Subtract the number of weeks, return the result
return $this->addWeeks($weeks * -1);
}
/**
* Subtract one, or the given number of days to the date interval specified as weeks.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $weeks [optional] The number of weeks to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subWeek($weeks = 1) {
return $this->subWeeks($weeks);
}
/**
* Set the number of days based on the given number of weeks.
* Note: This will change the number of days.
*
* @param int $weeks The number of weeks, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setWeeks($weeks) {
// Make sure the value is a positive number or zero
if(!is_int($weeks) || $weeks < 0)
throw new DomainException('Invalid value for months, must be zero or a positive number (\'' . $weeks .
'\' was given)');
// Set the days, return this instance
$this->d = $weeks * DateTime::DAYS_PER_WEEK;
return $this;
}
/**
* Get the number of days.
*
* @return int Number of days.
*/
public function getDays() {
return $this->d;
}
/**
* Add the given number of days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $days [optional] The number of days to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addDays($days = 1) {
// Make sure the days parameter is an integer
if(!is_int($days))
throw new DomainException('Invalid value for days, must be an integer (\'' . $days . '\' was given)');
// Calculate the new number of days
$days = $this->getDays() + intval($days);
// Make sure the number isn't negative
if($days < 0)
throw new Exception('The resulting number of days may not be negative (' . $days . ' is negative)');
// Set the number of days
$this->setDays($days);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $days [optional] The number of days to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addDay($days = 1) {
return $this->addDays($days);
}
/**
* Subtract the given number of days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $days [optional] The number of days to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subDays($days = 1) {
// Make sure the days parameter is an integer
if(!is_int($days))
throw new DomainException('Invalid value for days, must be an integer (\'' . $days . '\' was given)');
// Subtract the number of days, return the result
return $this->addDays($days * -1);
}
/**
* Subtract one, or the given number of days to the date interval.
* The resulting number of days may not be a negative number, or an exception will be thrown.
*
* @param int $days [optional] The number of days to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subDay($days = 1) {
return $this->subDays($days);
}
/**
* Set the number of days.
*
* @param int $days The number of days, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException Throws an exception on failure.
*/
public function setDays($days) {
// Make sure the value is a positive number or zero
if(!is_int($days) || $days < 0)
throw new DomainException('Invalid value for days, must be zero or a positive number (\'' . $days .
'\' was given)');
// Set the days, return this instance
$this->d = $days;
return $this;
}
/**
* Add the given number of hours, minutes and seconds to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $hours The number of hours to add to the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes to add to the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds to add to the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addTime($hours = null, $minutes = null, $seconds = null) {
// Add the number of hours, minutes and seconds if set
if(!empty($hours))
$this->addHours($hours);
if(!empty($minutes))
$this->addMinutes($minutes);
if(!empty($seconds))
$this->addSecond($seconds);
// Return this instance for method chaining
return $this;
}
/**
* Subtract the given number of hours, minutes and seconds to the date interval.
* The resulting values may not be a negative number, or an exception will be thrown.
*
* @param int|null $hours The number of hours to subtract to the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes to subtract to the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds to subtract to the interval. Null to ignore this value.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subTime($hours = null, $minutes = null, $seconds = null) {
// Subtract the number of hours, minutes and seconds if set
if(!empty($hours))
$this->subHours($hours);
if(!empty($minutes))
$this->subMinutes($minutes);
if(!empty($seconds))
$this->subSecond($seconds);
// Return this instance for method chaining
return $this;
}
/**
* Set the date and time part of the interval. This changes the specified years, months, days, hours, minutes and
* seconds.
*
* @param int|null $hours The number of hours of the interval. Null to ignore this value.
* @param int|null $minutes The number of minutes of the interval. Null to ignore this value.
* @param int|null $seconds The number of seconds of the interval. Null to ignore this value.
*
* @return static This DateInterval instance for method chaining.
*/
public function setTime($hours = null, $minutes = null, $seconds = null) {
// Set the hours, minutes and seconds
if(!empty($hours))
$this->setHours($hours);
if(!empty($minutes))
$this->setMinutes($minutes);
if(!empty($seconds))
$this->setSeconds($seconds);
// Return this instance
return $this;
}
/**
* Get the number of hours.
*
* @return int Number of hours.
*/
public function getHours() {
return $this->h;
}
/**
* Add the given number of hours to the date interval.
* The resulting number of hours may not be a negative number, or an exception will be thrown.
*
* @param int $hours [optional] The number of hours to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addHours($hours = 1) {
// Make sure the hours parameter is an integer
if(!is_int($hours))
throw new DomainException('Invalid value for hours, must be an integer (\'' . $hours . '\' was given)');
// Calculate the new number of hours
$hours = $this->getHours() + intval($hours);
// Make sure the number isn't negative
if($hours < 0)
throw new Exception('The resulting number of hours may not be negative (' . $hours . ' is negative)');
// Set the number of hours
$this->setHours($hours);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of hours to the date interval.
* The resulting number of hours may not be a negative number, or an exception will be thrown.
*
* @param int $hours [optional] The number of hours to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addHour($hours = 1) {
return $this->addHours($hours);
}
/**
* Subtract the given number of hours to the date interval.
* The resulting number of hours may not be a negative number, or an exception will be thrown.
*
* @param int $hours [optional] The number of hours to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subHours($hours = 1) {
// Make sure the hours parameter is an integer
if(!is_int($hours))
throw new DomainException('Invalid value for hours, must be an integer (\'' . $hours . '\' was given)');
// Subtract the number of hours, return the result
return $this->addHours($hours * -1);
}
/**
* Subtract one, or the given number of hours to the date interval.
* The resulting number of hours may not be a negative number, or an exception will be thrown.
*
* @param int $hours [optional] The number of hours to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subHour($hours = 1) {
return $this->subHours($hours);
}
/**
* Set the number of hours.
*
* @param int $hours The number of hours, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setHours($hours) {
// Make sure the value is a positive number or zero
if(!is_int($hours) || $hours < 0)
throw new DomainException('Invalid value for hours, must be zero or a positive number (\'' . $hours .
'\' was given)');
// Set the hours, return this instance
$this->h = $hours;
return $this;
}
/**
* Get the number of minutes.
*
* @return int Number of minutes.
*/
public function getMinutes() {
return $this->i;
}
/**
* Add the given number of minutes to the date interval.
* The resulting number of minutes may not be a negative number, or an exception will be thrown.
*
* @param int $minutes [optional] The number of minutes to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addMinutes($minutes = 1) {
// Make sure the minutes parameter is an integer
if(!is_int($minutes))
throw new DomainException('Invalid value for minutes, must be an integer (\'' . $minutes . '\' was given)');
// Calculate the new number of minutes
$minutes = $this->getMinutes() + intval($minutes);
// Make sure the number isn't negative
if($minutes < 0)
throw new Exception('The resulting number of minutes may not be negative (' . $minutes . ' is negative)');
// Set the number of minutes
$this->setMinutes($minutes);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of minutes to the date interval.
* The resulting number of minutes may not be a negative number, or an exception will be thrown.
*
* @param int $minutes [optional] The number of minutes to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addMinute($minutes = 1) {
return $this->addMinutes($minutes);
}
/**
* Subtract the given number of minutes to the date interval.
* The resulting number of minutes may not be a negative number, or an exception will be thrown.
*
* @param int $minutes [optional] The number of minutes to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subMinutes($minutes = 1) {
// Make sure the minutes parameter is an integer
if(!is_int($minutes))
throw new DomainException('Invalid value for minutes, must be an integer (\'' . $minutes .
'\' was given)');
// Subtract the number of minutes, return the result
return $this->addMinutes($minutes * -1);
}
/**
* Subtract one, or the given number of minutes to the date interval.
* The resulting number of minutes may not be a negative number, or an exception will be thrown.
*
* @param int $minutes [optional] The number of minutes to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subMinute($minutes = 1) {
return $this->subMinutes($minutes);
}
/**
* Set the number of minutes.
*
* @param int $minutes The number of minutes, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setMinutes($minutes) {
// Make sure the value is a positive number or zero
if(!is_int($minutes) || $minutes < 0)
throw new DomainException('Invalid value for minutes, must be zero or a positive number (\'' . $minutes .
'\' was given)');
// Set the minutes, return this instance
$this->i = $minutes;
return $this;
}
/**
* Get the number of seconds.
*
* @return int Number of seconds.
*/
public function getSeconds() {
return $this->s;
}
/**
* Add the given number of seconds to the date interval.
* The resulting number of seconds may not be a negative number, or an exception will be thrown.
*
* @param int $seconds [optional] The number of seconds to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addSeconds($seconds = 1) {
// Make sure the seconds parameter is an integer
if(!is_int($seconds))
throw new DomainException('Invalid value for seconds, must be an integer (\'' . $seconds . '\' was given)');
// Calculate the new number of seconds
$seconds = $this->getSeconds() + intval($seconds);
// Make sure the number isn't negative
if($seconds < 0)
throw new Exception('The resulting number of seconds may not be negative (' . $seconds . ' is negative)');
// Set the number of seconds
$this->setSeconds($seconds);
// Return this instance for method chaining
return $this;
}
/**
* Add one, or the given number of seconds to the date interval.
* The resulting number of seconds may not be a negative number, or an exception will be thrown.
*
* @param int $seconds [optional] The number of seconds to add.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function addSecond($seconds = 1) {
return $this->addSeconds($seconds);
}
/**
* Subtract the given number of seconds to the date interval.
* The resulting number of seconds may not be a negative number, or an exception will be thrown.
*
* @param int $seconds [optional] The number of seconds to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subSeconds($seconds = 1) {
// Make sure the seconds parameter is an integer
if(!is_int($seconds))
throw new DomainException('Invalid value for seconds, must be an integer (\'' . $seconds .
'\' was given)');
// Subtract the number of seconds, return the result
return $this->addSeconds($seconds * -1);
}
/**
* Subtract one, or the given number of seconds to the date interval.
* The resulting number of seconds may not be a negative number, or an exception will be thrown.
*
* @param int $seconds [optional] The number of seconds to subtract.
*
* @return static The DateTime instance for method chaining.
*
* @throws DomainException|Exception Throws an exception on failure.
*/
public function subSecond($seconds = 1) {
return $this->subSeconds($seconds);
}
/**
* Set the number of seconds.
*
* @param int $seconds The number of seconds, must be zero or a positive number.
*
* @return static The DateTime instance for method chaining.
*
* @throws \DomainException Throws an exception on failure.
*/
public function setSeconds($seconds) {
// Make sure the value is a positive number or zero
if(!is_int($seconds) || $seconds < 0)
throw new DomainException('Invalid value for seconds, must be zero or a positive number (\'' . $seconds .
'\' was given)');
// Set the seconds, return this instance
$this->s = $seconds;
return $this;
}
/**
* Check whether the date interval is inverted.
*
* @return bool True if the date interval is inverted, false if not.
*/
public function isInverted() {
return $this->invert !== 0;
}
/**
* Set whether the date interval is inverted.
*
* @param int|bool $inverted One or true if the date interval is inverted, zero or false if not.
*
* @return static This DateInterval instance.
*/
public function setInverted($inverted) {
// Parse the parameter value
$inverted = empty($inverted) ? 0 : 1;
// Set whether the interval is inverted, return this instance
$this->invert = $inverted;
return $this;
}
/**
* Get the day span of this interval. This only works for objects created with DateTime::diff().
*
* @return int|null The day span of this interval, or null on failure.
*/
public function getDaySpan() {
return $this->isCreatedFromDiff() ? $this->days : null;
}
/**
* Check whether this is a zero date interval.
*
* @return bool True if this date interval is zero, false if not.
*/
public function isZero() {
return ($this->getYears() == 0 && $this->getMonths() == 0 && $this->getDays() == 0 &&
$this->getHours() == 0 && $this->getMinutes() == 0 && $this->getSeconds() == 0);
}
/**
* Check whether this date time object was created using DateTime::diff() or PHPDateTime::diff().
*
* @return bool True if this date interval object was created by a diff() method, false if not. If the date
* interval isn't an instance of DateInterval false will also be returned.
*/
public function isCreatedFromDiff() {
return DateIntervalUtils::isCreatedFromDiff($this);
}
/**
* Add the passed interval of the current instance
*
* @param PHPDateInterval|PHPDateInterval|string|null $dateInterval The DateInterval or PHPDateInterval instance,
* the date interval specification as a string or null to use a zero specification.
*
* @return static|null This DateInterval instance for method chaining, or null on failure.
*/
public function add($dateInterval) {
// Parse the date interval, return null on failure
if(($dateInterval = static::parse($dateInterval)) === null)
return null;
// Calculate the factor to multiply each value with
$factor = $dateInterval->isInverted() ? -1 : 1;
/*if(DateIntervalUtils::isCreatedFromDiff($dateInterval))
$this->setDays($this->getDays() + ($dateInterval->getDaySpan() * $factor));
else*/
// Add the date and time
$this->addDateTime($dateInterval->getYears() * $factor, $dateInterval->getMonths() * $factor, null,
$dateInterval->getDays() * $factor, $dateInterval->getHours() * $factor,
$dateInterval->getMinutes() * $factor, $dateInterval->getSeconds() * $factor);
// Return this instance
return $this;
}
/**
* Subtract the passed interval of the current instance.
*
* @param PHPDateInterval|PHPDateInterval|string|null $dateInterval The DateInterval or PHPDateInterval instance,
* the date interval specification as a string or null to use a zero specification.
*
* @return static|null This DateInterval instance for method chaining, or null on failure.
*/
public function sub($dateInterval) {
// Parse the date interval as a new instance, return null on failure
if(($dateInterval = static::instance($dateInterval)) === null)
return null;
// Invert the date interval
$dateInterval->setInverted(!$dateInterval->isInverted());
// Subtract and return the result
return $this->add($dateInterval);
}
/**
* Create a ISO-8601 date interval specification string.
*
* @return string The date interval specification string.
*/
public function toSpecString() {
return PHPDateInterval::create($this->getYears(), $this->getMonths(), null, $this->getDays(),
$this->getHours(), $this->getMinutes(), $this->getSeconds());
}
/**
* Get the date interval as a string.
*
* @return string The date interval string.
*/
public function toString() {
return $this->toSpecString();
}
// TODO: toString() for humans!
/**
* Get the date interval as a string.
*
* @return string The date interval string.
*/
public function __toString() {
return ($result = $this->toString()) === null ? '' : $result;
}
/*
/**
* Get a date interval attribute.
*
* @param string $name The name of the attribute to get.
*
* @return int The value of the getter.
*
* @throws \InvalidArgumentException Throws an exception on failure.
* /
public function __get($name) {
// Get the number of years
if(StringUtils::equals($name, 'years', true))
return $this->getYears();
// Get the number of months
if(StringUtils::equals($name, 'months', true))
return $this->getMonths();
// Get the number of days
if(StringUtils::equals($name, 'days', true))
return $this->getDays();
// Get the number of hours
if(StringUtils::equals($name, 'hours', true))
return $this->getHours();
// Get the number of minutes
if(StringUtils::equals($name, 'minutes', true))
return $this->getMinutes();
// Get the number of seconds
if(StringUtils::equals($name, 'seconds', true))
return $this->getSeconds();
switch($name) {
case 'weeks':
return (int) floor($this->d / DateTime::DAYS_PER_WEEK);
case 'daysExcludeWeeks':
return $this->d % DateTime::DAYS_PER_WEEK;
default:
throw new \InvalidArgumentException('Unknown getter \'' . $name . '\'');
}
}
/**
* Set an attribute of the date interval object.
*
* @param string $name The name of the attribute to set.
* @param int $value The value to set the attribute to.
*
* @return static The DateTime instance.
*
* @throws \InvalidArgumentException Throw an exception on failure.
* /
public function __set($name, $value) {
// Set the number of years
if(StringUtils::equals($name, 'years', true))
return $this->setYears($value);
// Set the number of months
if(StringUtils::equals($name, 'months', true))
return $this->setMonths($value);
// Set the number of days
if(StringUtils::equals($name, 'days', true))
return $this->setDays($value);
// Set the number of hours
if(StringUtils::equals($name, 'hours', true))
return $this->setHours($value);
// Set the number of minutes
if(StringUtils::equals($name, 'minutes', true))
return $this->setMInutes($value);
// Set the number of seconds
if(StringUtils::equals($name, 'seconds', true))
return $this->setSeconds($value);
switch ($name) {
case 'weeks':
$this->d = $value * DateTime::DAYS_PER_WEEK;
break;
}
}
/**
* Allow fluent calls on the setters. CarbonInterval::years(3)->months(5)->day().
*
* Note: This is done using the magic method to allow static and instance methods to have the same names.
*
* @param string $name The name of the method being called.
* @param Array $args An array of method parameters.
*
* @return static The DateTime instance, which could be used for method chaining.
* /
public function __call($name, $args) {
// Get the delimiter value
$arg = count($args) == 0 ? 1 : $args[0];
// Handle the years setter
if(StringUtils::equals($name, Array('years', 'year'), true)) {
$this->years = $arg;
return $this;
}
// Handle the months setter
if(StringUtils::equals($name, Array('months', 'month'), true)) {
$this->months = $arg;
return $this;
}
// Handle the weeks setter
if(StringUtils::equals($name, Array('weeks', 'week'), true)) {
$this->days = $arg * DateTime::DAYS_PER_WEEK;
return $this;
}
// Handle the days setter
if(StringUtils::equals($name, Array('days', 'day'), true)) {
$this->days = $arg;
return $this;
}
// Handle the hours setter
if(StringUtils::equals($name, Array('hours', 'hour'), true)) {
$this->hours = $arg;
return $this;
}
// Handle the minutes setter
if(StringUtils::equals($name, Array('minutes', 'minute'), true)) {
$this->minutes = $arg;
return $this;
}
// Handle the seconds setter
if(StringUtils::equals($name, Array('seconds', 'second'), true)) {
$this->seconds = $arg;
return $this;
}
// Return this instance
return $this;
}
/**
* Provide static helpers to create instances.
* Allows CarbonInterval::years(3).
*
* Note: This is done using the magic method to allow static and instance methods to have the same names.
*
* @param string $name The name of the static method being called.
* @param Array $args An array of method parameters.
*
* @return static The DateInterval instance.
* /
public static function __callStatic($name, $args) {
// Get the delimiter value if any argument has been given
$delimiterValue = count($args) == 0 ? 1 : $args[0];
// Handle the years delimiter
if(StringUtils::equals($name, Array('years', 'year'), true))
return DateIntervalFactory::create($delimiterValue);
// Handle the months delimiter
if(StringUtils::equals($name, Array('months', 'month'), true))
return DateIntervalFactory::create(null, $delimiterValue);
// Handle the weeks delimiter
if(StringUtils::equals($name, Array('weeks', 'week'), true))
return DateIntervalFactory::create(null, null, $delimiterValue);
// Handle the days delimiter
if(StringUtils::equals($name, Array('days', 'day'), true))
return DateIntervalFactory::create(null, null, null, $delimiterValue);
// Handle the hours delimiter
if(StringUtils::equals($name, Array('hours', 'hour'), true))
return DateIntervalFactory::create(null, null, null, null, $delimiterValue);
// Handle the minutes delimiter
if(StringUtils::equals($name, Array('minutes', 'minute'), true))
return DateIntervalFactory::create(null, null, null, null, null, $delimiterValue);
// Handle the seconds delimiter
if(StringUtils::equals($name, Array('seconds', 'second'), true))
return DateIntervalFactory::create(null, null, null, null, null, null, $delimiterValue);
}
*/
}
<?php
namespace carbon\core\datetime\interval;
// Prevent direct requests to this set_file due to security reasons
use carbon\core\datetime\interval\spec\DateIntervalSpecFactory;
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateIntervalFactory {
/**
* Create a new DateInterval instance from specific values.
*
* @param int $years [optional] The number of years, or null to ignore this value.
* @param int $months [optional] The number of months, or null to ignore this value.
* @param int $weeks [optional] The number of weeks, or null to ignore this value.
* @param int $days [optional] The number of days, or null to ignore this value.
* @param int $hours [optional] The number of hours, or null to ignore this value.
* @param int $minutes [optional] The number of minutes, or null to ignore this value.
* @param int $seconds [optional] The number of seconds, or null to ignore this value.
* @param bool $inverted [optional] Define whether the date interval is inverted or not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed A new DateInterval instance, or the default value on failure.
*/
public static function create($years = null, $months = null, $weeks = null, $days = null, $hours = null,
$minutes = null, $seconds = null, $inverted = false, $default = null) {
// Create the specification, and make sure it's valid
if(($spec = DateIntervalSpecFactory::create($years, $months, $weeks, $days, $hours, $minutes, $seconds)) === null)
return $default;
// Construct the DateInterval object
return new DateInterval($spec, $inverted);
}
/**
* Create a date interval object of zero.
*
* @return DateInterval|null The DateInterval object.
*/
public static function zero() {
return static::create();
}
/**
* Create a date interval object for one, or the given number of years.
*
* @param int $years [optional] The number of years. Null to use one year.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function year($years = 1, $inverted = false, $default = null) {
// Parse the year parameter, and make sure it's valid
if($years === null)
$years = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $years, null, null, null, $inverted, $default);
}
/**
* Alias of year();
*
* Create a date interval object for one, or the given number of years.
*
* @param int $years [optional] The number of years. Null to use one year.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function years($years = 1, $inverted = false, $default = null) {
return static::year($years, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of months.
*
* @param int $months [optional] The number of months. Null to use one month.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function month($months = 1, $inverted = false, $default = null) {
// Parse the month parameter, and make sure it's valid
if($months === null)
$months = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $months, null, null, null, $inverted, $default);
}
/**
* Alias of month();
*
* Create a date interval object for one, or the given number of months.
*
* @param int $months [optional] The number of months. Null to use one month.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function months($months = 1, $inverted = false, $default = null) {
return static::month($months, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of weeks.
*
* @param int $weeks [optional] The number of weeks. Null to use one week.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function week($weeks = 1, $inverted = false, $default = null) {
// Parse the week parameter, and make sure it's valid
if($weeks === null)
$weeks = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $weeks, null, null, null, $inverted, $default);
}
/**
* Alias of week();
*
* Create a date interval object for one, or the given number of weeks.
*
* @param int $weeks [optional] The number of weeks. Null to use one week.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function weeks($weeks = 1, $inverted = false, $default = null) {
return static::week($weeks, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of days.
*
* @param int $days [optional] The number of days. Null to use one day.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function day($days = 1, $inverted = false, $default = null) {
// Parse the day parameter, and make sure it's valid
if($days === null)
$days = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $days, null, null, null, $inverted, $default);
}
/**
* Alias of day();
*
* Create a date interval object for one, or the given number of days.
*
* @param int $days [optional] The number of days. Null to use one day.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function days($days = 1, $inverted = false, $default = null) {
return static::day($days, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of hours.
*
* @param int $hours [optional] The number of hours. Null to use one hour.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function hour($hours = 1, $inverted = false, $default = null) {
// Parse the hour parameter, and make sure it's valid
if($hours === null)
$hours = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $hours, null, null, null, $inverted, $default);
}
/**
* Alias of hour();
*
* Create a date interval object for one, or the given number of hours.
*
* @param int $hours [optional] The number of hours. Null to use one hour.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function hours($hours = 1, $inverted = false, $default = null) {
return static::hour($hours, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of minutes.
*
* @param int $minutes [optional] The number of minutes. Null to use one minute.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function minute($minutes = 1, $inverted = false, $default = null) {
// Parse the minute parameter, and make sure it's valid
if($minutes === null)
$minutes = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $minutes, null, null, null, $inverted, $default);
}
/**
* Alias of minute();
*
* Create a date interval object for one, or the given number of minutes.
*
* @param int $minutes [optional] The number of minutes. Null to use one minute.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function minutes($minutes = 1, $inverted = false, $default = null) {
return static::minute($minutes, $inverted, $default);
}
/**
* Create a date interval object for one, or the given number of seconds.
*
* @param int $seconds [optional] The number of seconds. Null to use one second.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function second($seconds = 1, $inverted = false, $default = null) {
// Parse the second parameter, and make sure it's valid
if($seconds === null)
$seconds = 1;
// Create and return a new DateInterval instance, or return the default value on failure
return static::create(null, null, null, $seconds, null, null, null, $inverted, $default);
}
/**
* Alias of second();
*
* Create a date interval object for one, or the given number of seconds.
*
* @param int $seconds [optional] The number of seconds. Null to use one second.
* @param bool $inverted [optional] True to invert the date interval, false if not.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateInterval|mixed The DateInterval instance, or the default value on failure.
*/
public static function seconds($seconds = 1, $inverted = false, $default = null) {
return static::second($seconds, $inverted, $default);
}
}
<?php
namespace carbon\core\datetime\interval;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateIntervalSet {
//private $intervals = Array();
// TODO: Create this class!
}
<?php
namespace carbon\core\datetime\interval;
use DateInterval as PHPDateInterval;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateIntervalUtils {
/**
* Before PHP 5.4.20/5.5.4 instead of false days will be set to -99999 when the interval instance was created by DateTime:diff().
*
* @const int The old PHP false value of days.
*/
const PHP_OLD_FALSE_DAYS = -99999;
/**
* Parse a date interval. A new instance may be created.
*
* This method allows better fluent syntax because it makes method chaining possible.
*
* @param DateInterval|PHPDateInterval|string|null $dateInterval [optional] A DateInterval or PHPDateInterval instance, a date interval specification, or null to use a zero specification.
* @param mixed|null $default [optional] The default value to be returned on failure.
*
* @return static|mixed A DateInterval instance, or the default value on failure.
*/
public static function parse($dateInterval, $default = null) {
return ($result = DateInterval::parse($dateInterval)) === null ? $default : $result;
}
/**
* Check whether this date time object was created using DateTime::diff() or PHPDateTime::diff().
*
* @param DateInterval|PHPDateInterval $dateInterval The DateInterval or PHPDateInterval instance.
*
* @return bool True if this date interval object was created by a diff() method, false if not. If the date interval isn't an instance of DateInterval false will also be returned.
*/
// TODO: Improve the performance of this method, just check whether it's false or -99999?
public static function isCreatedFromDiff($dateInterval) {
// Make sure the date interval isn't null
if($dateInterval == null)
return false;
// Make sure the date interval is a DateInterval or PHPDateInterval instance
if(!($dateInterval instanceof PHPDateInterval))
return false;
// Get the value to compare the days to, this differs depending on the installed PHP version
$compareTo = (
version_compare(phpversion(), '5.4.20', '<') ||
(version_compare(phpversion(), '5.0', '>=') && version_compare(phpversion(), '5.5.4', '<'))
) ? static::PHP_OLD_FALSE_DAYS : false;
// Make sure the number of days, return the result
return $dateInterval->days != $compareTo;
}
}
<?php
/**
* Created by PhpStorm.
* User: Tim
* Date: 3-5-2015
* Time: 18:09
*/
namespace carbon\core\datetime\interval\spec;
// Prevent direct requests to this set_file due to security reasons
use carbon\core\datetime\DateTime;
use carbon\core\datetime\interval\DateInterval;
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateIntervalSpecFactory {
/**
* Create a date interval specification based on the given parameters.
*
* @param int|null $years [optional] The number of years, must be zero or a positive number. Null to ignore this value.
* @param int|null $months [optional] The number of months, must be zero or a positive number. Null to ignore this value.
* @param int|null $weeks [optional] The number of weeks, must be zero or a positive number. Null to ignore this value. The weeks are converted into days.
* @param int|null $days [optional] The number of days, must be zero or a positive number. Null to ignore this value.
* @param int|null $hours [optional] The number of hours, must be zero or a positive number. Null to ignore this value.
* @param int|null $minutes [optional] The number of minutes, must be zero or a positive number. Null to ignore this value.
* @param int|null $seconds [optional] The number of seconds, must be zero or a positive number. Null to ignore this value.
*
* @return string|null The date interval spec as a string, or null on failure.
*/
// TODO: Allow a default value!
public static function create($years = null, $months = null, $weeks = null, $days = null, $hours = null,
$minutes = null, $seconds = null) {
// Build the date interval specification string
$dateIntervalSpec = DateInterval::PERIOD_PREFIX;
// Check whether the weeks parameter is used, append the number of days
if(!empty($weeks))
$days += (int) ($weeks * DateTime::DAYS_PER_WEEK);
// Reading all non-zero date parts
$date = array_filter(array(
DateInterval::PERIOD_YEARS => $years,
DateInterval::PERIOD_MONTHS => $months,
DateInterval::PERIOD_DAYS => $days
));
// Reading all non-zero time parts
$time = array_filter(array(
DateInterval::PERIOD_HOURS => $hours,
DateInterval::PERIOD_MINUTES => $minutes,
DateInterval::PERIOD_SECONDS => $seconds
));
// Make sure at least one part is available
if(empty($date) && empty($time))
$date = array(DateInterval::PERIOD_YEARS => 0);
// Append each date part to the specification string
foreach($date as $key => $value)
$dateIntervalSpec .= $value . $key;
// Append each time part to the specification string if available
if(!empty($time)) {
// Prefix the time designator
$dateIntervalSpec .= DateInterval::PERIOD_TIME_PREFIX;
// Append each time part to the specification string
foreach($time as $key => $value)
$dateIntervalSpec .= $value . $key;
}
// Return the spec if it's valid, return null otherwise
return DateIntervalSpecUtils::isValid($dateIntervalSpec) ? $dateIntervalSpec : null;
}
/**
* Create a date interval specification of zero.
*
* @return string The date interval specification.
*/
public static function zero() {
return static::create();
}
/**
* Create a date interval specification for one, or the given number of years.
*
* @param int $years [optional] The number of years. Null to use one year.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function year($years = 1, $default = null) {
// Parse the year parameter, and make sure it's valid
if($years === null)
$years = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $years, null, null, null, $default);
}
/**
* Alias of year();
*
* Create a date interval specification for one, or the given number of years.
*
* @param int $years [optional] The number of years. Null to use one year.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function years($years = 1, $default = null) {
return static::year($years, $default);
}
/**
* Create a date interval specification for one, or the given number of months.
*
* @param int $months [optional] The number of months. Null to use one month.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function month($months = 1, $default = null) {
// Parse the month parameter, and make sure it's valid
if($months === null)
$months = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $months, null, null, null, $default);
}
/**
* Alias of month();
*
* Create a date interval specification for one, or the given number of months.
*
* @param int $months [optional] The number of months. Null to use one month.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function months($months = 1, $default = null) {
return static::month($months, $default);
}
/**
* Create a date interval specification for one, or the given number of weeks.
*
* @param int $weeks [optional] The number of weeks. Null to use one week.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function week($weeks = 1, $default = null) {
// Parse the week parameter, and make sure it's valid
if($weeks === null)
$weeks = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $weeks, null, null, null, $default);
}
/**
* Alias of week();
*
* Create a date interval specification for one, or the given number of weeks.
*
* @param int $weeks [optional] The number of weeks. Null to use one week.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function weeks($weeks = 1, $default = null) {
return static::week($weeks, $default);
}
/**
* Create a date interval specification for one, or the given number of days.
*
* @param int $days [optional] The number of days. Null to use one day.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function day($days = 1, $default = null) {
// Parse the day parameter, and make sure it's valid
if($days === null)
$days = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $days, null, null, null, $default);
}
/**
* Alias of day();
*
* Create a date interval specification for one, or the given number of days.
*
* @param int $days [optional] The number of days. Null to use one day.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function days($days = 1, $default = null) {
return static::day($days, $default);
}
/**
* Create a date interval specification for one, or the given number of hours.
*
* @param int $hours [optional] The number of hours. Null to use one hour.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function hour($hours = 1, $default = null) {
// Parse the hour parameter, and make sure it's valid
if($hours === null)
$hours = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $hours, null, null, null, $default);
}
/**
* Alias of hour();
*
* Create a date interval specification for one, or the given number of hours.
*
* @param int $hours [optional] The number of hours. Null to use one hour.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function hours($hours = 1, $default = null) {
return static::hour($hours, $default);
}
/**
* Create a date interval specification for one, or the given number of minutes.
*
* @param int $minutes [optional] The number of minutes. Null to use one minute.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function minute($minutes = 1, $default = null) {
// Parse the minute parameter, and make sure it's valid
if($minutes === null)
$minutes = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $minutes, null, null, null, $default);
}
/**
* Alias of minute();
*
* Create a date interval specification for one, or the given number of minutes.
*
* @param int $minutes [optional] The number of minutes. Null to use one minute.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function minutes($minutes = 1, $default = null) {
return static::minute($minutes, $default);
}
/**
* Create a date interval specification for one, or the given number of seconds.
*
* @param int $seconds [optional] The number of seconds. Null to use one second.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function second($seconds = 1, $default = null) {
// Parse the second parameter, and make sure it's valid
if($seconds === null)
$seconds = 1;
// Create and return a new date interval specification, or return the default value on failure
return static::create(null, null, null, $seconds, null, null, null, $default);
}
/**
* Alias of second();
*
* Create a date interval specification for one, or the given number of seconds.
*
* @param int $seconds [optional] The number of seconds. Null to use one second.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The date interval specification, or the default value on failure.
*/
public static function seconds($seconds = 1, $default = null) {
return static::second($seconds, $default);
}
}
<?php
namespace carbon\core\datetime\interval\spec;
use carbon\core\datetime\DateTime;
use carbon\core\datetime\interval\DateInterval;
use DateInterval as PHPDateInterval;
use Exception;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateIntervalSpecUtils {
/**
* Defines the regex to use to validate a date interval specification string according to ISO-8601.
*
* @const string The date interval validation regex.
*/
const DATE_INTERVAL_SPEC_REGEX = '/^\s*P((((([0-9]+Y([0-9]+M)?([0-9]+[DW])?)|([0-9]+M([0-9]+[DW])?)|([0-9]+[DW]))(T(([0-9]+H([0-9]+M)?([0-9]+S)?)|([0-9]+M([0-9]+S)?)|([0-9]+S)))?|(T(([0-9]+H([0-9]+M)?([0-9]+S)?)|([0-9]+M([0-9]+S)?)|([0-9]+S)))))|(([0-9]{4}-(0[0-9]|1[0-2])-([0-2][0-9]|3[0-1]))T(([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])))\s*$/';
/**
* Try to parse a date interval specification string.
*
* @param string|DateInterval|PHPDateInterval|null $dateIntervalSpec [optional] A date interval specification, a
* relative date and time string, a DateInterval or PHPDateInterval instance or null to create a zero
* configuration.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|mixed The parsed date interval specification or the default value on failure.
*/
public static function parse($dateIntervalSpec = null, $default = null) {
// Return a zero specification if the specification is set to null
if($dateIntervalSpec === null)
return DateIntervalSpecFactory::zero();
// Parse strings
if(is_string($dateIntervalSpec)) {
// Check whether the string is already a valid specification
if(static::isValid($dateIntervalSpec))
return $dateIntervalSpec;
// Check whether the string has relative keywords
if(DateTime::hasRelativeKeywords($dateIntervalSpec)) {
try {
// Try to parse the string as relative date and time
$dateInterval = DateInterval::createFromDateString($dateIntervalSpec);
// Get and return the date interval specification
return $dateInterval->toSpecString();
} catch(Exception $ex) { }
}
}
// Parse DateInterval objects
if($dateIntervalSpec instanceof DateInterval)
return $dateIntervalSpec->toSpecString();
// Parse PHPDateInterval objects
if($dateIntervalSpec instanceof PHPDateInterval)
return DateIntervalSpecFactory::create($dateIntervalSpec->y, $dateIntervalSpec->m, null, $dateIntervalSpec->d,
$dateIntervalSpec->h, $dateIntervalSpec->i, $dateIntervalSpec->s);
// Couldn't parse the string, return the default value
return $default;
}
/**
* Validate whether a date interval specification string is valid or not based on ISO-8601.
*
* @param string $dateIntervalSpec The date interval specification string to validate.
*
* @return bool True if the date interval specification is valid, false otherwise.
*/
public static function isValid($dateIntervalSpec) {
// Make sure the param is a string
if(!is_string($dateIntervalSpec))
return false;
// Check whether the specification is valid using a regular expression, return the result
return preg_match(static::DATE_INTERVAL_SPEC_REGEX, $dateIntervalSpec) > 0;
}
}
<?php
namespace carbon\core\datetime\period;
use DatePeriod as PHPDatePeriod;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DatePeriod extends PHPDatePeriod {
// TODO: Parse method!
}
<?php
namespace carbon\core\datetime\zone;
use carbon\core\datetime\DateTime;
use carbon\core\datetime\DateTimeUtils;
use carbon\core\util\StringUtils;
use DateTime as PHPDateTime;
use DateTimeZone as PHPDateTimeZone;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateTimeZone extends PHPDateTimeZone {
// TODO: Make sure all DateTimeZone and PHPDateTimeZone instances are correct!
/**
* Constructor.
*
* @param string|PHPDateTimeZone|PHPDateTimeZone|null $timezone [optional] The timezone as a string, or as DateTimeZone or PHPDateTimeZone instance. Null to use the default timezone.
*
* @throws \Exception Throws an exception on failure.
*/
// TODO: Does this constructor still throw an error on failure?
public function __construct($timezone) {
// Use the default timezone if the parameter is null
if($timezone === null)
$timezone = DateTimeZoneUtils::getDefaultTimezone();
// If the timezone is already a DateTimeZone or PHPDateTimeZone instance, copy it
if($timezone instanceof parent) {
parent::__construct($timezone->getName());
return $this;
}
// Try to construct the parent object
parent::__construct($timezone);
return $this;
}
/**
* Parse a timezone. A new instance will be created if required.
*
* @param PHPDateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone The timezone to parse as DateTimeZone or PHPDateTimeZone instance or as a string. A DateTime or PHPDateTime instance to use it's timezone. Null to parse as the default timezone.
*
* @return PHPDateTimeZone|null The parsed timezone as DateTimeZone instance, or null on failure.
*/
public static function parse($timezone = null) {
return DateTimeZoneUtils::parse($timezone, null);
}
/**
* Check whether this timezone equals another timezone.
*
* @param PHPDateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime $timezone The other timezone as DateTimeZone or PHPDateTimeZone instance, or the timezone ID as a string. A DateTime or PHPDateTime instance may be supplied to use it's timezone.
*
* @return bool True if the timezones equal, false if not. False will also be returned if an error occurred.
*/
public function equals($timezone) {
// Parse the timezone
if(($timezone = static::parse($timezone)) === null)
return false;
// Compare the timezone IDs of both instances, return the result
return StringUtils::equals($this->getName(), $timezone->getName(), false);
}
/**
* Check whether a timezone is local relative to this timezone. This compares the offset of both timezones at a specific point in time.
*
* @param PHPDateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone [optional] A DateTimeZone or PHPDateTimeZone instance, the timezone ID as a string, a DateTime or PHPDateTime instance to use it's timezone or null to use the defualt timezone.
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The specific point in date and time to compare the offsets at. A DateTime or PHPDateTime instance, the date and time as a string or null to use the now() value.
*
* @return bool True if the timezone is local, false if not. False will also be returned on failure.
*/
public function isLocal($timezone = null, $dateTime = null) {
// Parse the timezone, return false on failure
if(($timezone = static::parse($timezone)) === null)
return false;
// Parse the date and time, return false on failure
if(($dateTime = DateTimeUtils::parse($dateTime, $timezone, null)) === null)
return false;
// Compare the offsets of both timezones at the specific point in time, return the result
return $this->getOffset($dateTime) == $timezone->getOffset($dateTime);
}
}
<?php
namespace carbon\core\datetime\zone;
use carbon\core\datetime\DateTime;
use DateTime as PHPDateTime;
use DateTimeZone as PHPDateTimeZone;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateTimeZoneUtils {
// TODO: Make sure all DateTimeZone and PHPDateTimeZone instances are correct!
// TODO: TimeZone or Timezone? Use only one of the two, not both!
/**
* Parse a timezone. A new instance will be created if required.
*
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone The timezone to parse as DateTimeZone or PHPDateTimeZone instance or as a string. A DateTime or PHPDateTime instance to use it's timezone. Null to parse as the default timezone.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return DateTimeZone|mixed The parsed timezone as DateTimeZone instance, or the default value on failure.
*/
public static function parse($timezone = null, $default = null) {
// Return the timezone if it's already a DateTimeZone instance
if($timezone instanceof DateTimeZone)
return $timezone;
// Get the timezone form a DateTime object
else if($timezone instanceof DateTime)
$timezone = $timezone->getTimezone();
// Get the timezone from a PHPDateTimeZone object, and make sure it's valid
else if($timezone instanceof PHPDateTime)
if(($timezone = static::parse($timezone->getTimezone())) === null)
return $default;
// If the timezone is a string, make sure the timezone ID is valid, return the default value if not
// TODO: Should we check this for everything, or just for strings?
if(is_string($timezone))
if(!static::isValidTimezoneId($timezone))
return $default;
// Try to parse and return the timezone
try {
return new DateTimeZone($timezone);
} catch(\Exception $ex) {
// Return the default value on failure
return $default;
}
}
// TODO: equals(...); method (also to the base class)!
/**
* Get the default timezone for all date and time functions.
*
* @return DateTimeZone The default timezone.
*/
public static function getDefaultTimezone() {
return static::parse(date_default_timezone_get());
}
/**
* Set the default timezone for all date and time functions.
*
* @param DateTimeZone|PHPDateTimeZone|string|null $timezone The timezone as DateTimeZone instance, the timezone ID as a string or null to use the default timezone configured in PHPs configuration file.
*
* @return bool True if succeed, false on failure.
*/
public static function setDefaultTimezone($timezone) {
// Check whether the default timezone should be restored
if($timezone === null)
// TODO: Read the real-default timezone from other sources if possible
$timezone = ini_get('date.timezone');
// Parse the timezone and make sure it's valid
if(($timezone = static::parse($timezone, null)) === null)
return false;
// Set the default timezone
date_default_timezone_set($timezone->getName());
}
/**
* Check whether a timezone ID is valid. A timezone ID is specified by a string, see PHPs list of supported timezones.
*
* @param string $timezone The timezone ID to validate.
*
* @return bool True if the timezone ID is valid, false otherwise.
*/
// TODO: Can we improve the speed and readability of this method?
public static function isValidTimezoneId($timezone) {
// Create a list of valid timezones
$valid = array();
// Get the list of supported PHP timezones
$timezoneList = timezone_abbreviations_list();
// Create a list of valid timezones
foreach($timezoneList as $zone)
foreach($zone as $item)
$valid[$item['timezone_id']] = true;
// Remove the invalid keys
unset($valid['']);
// Check weather the timezone ID exists as a key in the array, return the result
return !!$valid[$timezone];
}
/**
* Check whether the timezones a and b are equal to each other.
*
* @param DateTimeZone|PHPDateTimeZone|string|null $a [optional] The timezone as DateTimeZone or PHPDateTimeZone instance, the timezone ID as a string or null to use the default timezone.
* @param DateTimeZone|PHPDateTimeZone|string|null $b [optional] The timezone as DateTimeZone or PHPDateTimeZone instance, the timezone ID as a string or null to use the default timezone.
* @param null|mixed $default [optional] The default value returned on failure.
*
* @return bool|mixed True if the timezones are equal, false if not. The default value will be returned on failure.
*/
public static function equals($a = null, $b = null, $default = null) {
// Parse the timezone of a, return the default value on failure
if(($a = static::parse($a, null)) === null)
return $default;
// Check whether the timezones are equal, return the result
return $a->equals($b);
}
/**
* Check whether the timezone a and b are local. This compares the offset of both timezones at a specific point in time.
*
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $a [optional] A DateTimeZone or PHPDateTimeZone instance, the timezone ID as a string, a DateTime or PHPDateTime instance to use it's timezone or null to use the defualt timezone.
* @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $b [optional] A DateTimeZone or PHPDateTimeZone instance, the timezone ID as a string, a DateTime or PHPDateTime instance to use it's timezone or null to use the defualt timezone.
* @param DateTime|PHPDateTime|string|null $dateTime [optional] The specific point in date and time to compare the offsets at. A DateTime or PHPDateTime instance, the date and time as a string or null to use the now() value.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return bool True if the timezone is local, false if not. The default value will be returned on failure.
*/
public function isLocal($a = null, $b = null, $dateTime = null, $default = null) {
// Parse the timezone of a, return false on failure
if(($a = static::parse($a)) === null)
return $default;
// Check whether the timezone of a and b are local, return the result or return the default value on failure
return ($result = $a->isLocal($b, $dateTime)) === null ? $default : $result;
}
/**
* Creates a DateTimeZone from a string or a PHPDateTimeZone.
*
* @param PHPDateTimeZone|string|null $timeZone
*
* @return PHPDateTimeZone
*
* @throws \InvalidArgumentException
*/
// TODO: Should we keep this method?
public static function safeCreateDateTimeZone($timeZone) {
// Return the default time zone if nothing was supplied
if($timeZone === null)
// Don't return null to avoid the PHP bug #52063 (< v5.3.6)
return static::getDefaultTimezone();
// Immediately return the object if it's already a time zone instance
if($timeZone instanceof PHPDateTimeZone)
return $timeZone;
// Try to parse the timezone, throw an exception on failure
if(($timeZone = static::parse($timeZone, null)) === null)
// Failed to create a time zone object, thrown an exception
throw new \InvalidArgumentException('Unknown or bad timezone ('.$timeZone.')');
return $timeZone;
}
}
<?php
/**
* ErrorHandler.php
*
* The ErrorHandler class handles all the errors and exceptions.
* The ErrorHandler class shows a nice, informative error page when the debug mode enabled.
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\exception\CarbonException;
use carbon\core\util\ArrayUtils;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
// TODO: Make sure the error handler is not showing sensitive data!
// TODO: Show Carbon CMS name and logo on the error page
// TODO: Use a 'noob friendly' explanation on the error page
// TODO: Add feature to automatically send the error to administrators
// TODO: Clean up the code, improve everything
// TODO: Fix fatal errors not being handled (reproduce using: require("sdfjasopdifaspoid");)
// TODO: Add method to restore the error handler using restore_exception_handler();
/**
* Handles all the errors and exceptions.
* @package core
* @author Tim Visee
*/
class ErrorHandler {
/** @var bool $debug True to enable debug mode, sensitive data will be shown when this mode is enabled */
private static $debug = false;
/**
* Initialize the error handler
* @param bool $handle_exceptions [optional]True to handle all exceptions
* @param bool $handle_errors [optional] True to handle all errors
* @param bool $debug [optional] True to enable the debug mode, sensitive data will be shown of this mode is enabled
*/
public static function init($handle_exceptions = true, $handle_errors = true, $debug = false) {
// Set whether the debug mode should be enabled or not
self::$debug = $debug;
// TODO: Should we include the 'E_ALL' param the function bellow?
// Set the error and exception handlers
if($handle_errors)
set_error_handler(__CLASS__ . '::handleError', E_ALL);
if($handle_exceptions)
set_exception_handler(__CLASS__ . '::handleException');
// TODO: Improve this code!
register_shutdown_function(function() {
$isError = false;
if ($error = error_get_last()){
switch($error['type']){
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$isError = true;
static::handleError($error['type'], $error['message'], $error['file'], $error['line'], null);
break;
}
}
if ($isError){
//var_dump ($error);//do whatever you need with it
}
});
}
/**
* Get whether the debug mode is enabled or not
* @return bool True if the debug mode is enabled, false otherwise
*/
public static function getDebug() {
return self::$debug;
}
/**
* Set whether the debug mode is enabled or not. Sensitive data will be shown if this mode is enabled.
* @param bool $debug True to enable the debug mode, false otherwise
*/
public static function setDebug($debug) {
self::$debug = $debug;
}
/**
* Handles all errors. Pushes an ErrorException based on the error to the handleException method.
* @param int $err_code Error code
* @param string $err_msg Error message
* @param string $err_file Error source set_file
* @param int $err_line Line the error source is at in the source set_file
* @param mixed $err_context Error context
*/
public static function handleError($err_code, $err_msg, $err_file, $err_line, $err_context = null) {
// Ignore errors with error code 8 (E_NOTICE, since these could also be called when there'statements no problem at all)
if($err_code === 8)
return;
// Push an ErrorException based on the error to the handleException method
self::handleException(new \ErrorException($err_msg, 0, $err_code, $err_file, $err_line));
// TODO: Should return false?
}
/**
* Handles all PHP exceptions
* @param \Exception $ex Exception instance
*/
public static function handleException(\Exception $ex) {
// End and clean any nesting of the output buffering mechanism
while(ob_get_level() > 0)
ob_end_clean();
// Get the exception type
$ex_type = get_class($ex);
// Get the page title, make sure no sensitive data is being shown
// TODO: Use better title!
if(self::getDebug())
$page_title = $ex_type . ': ' . $ex->getMessage();
else
$page_title = 'Error!';
// Print the top of the page
self::printPageTop($page_title);
// Show the information message
?>
<div id="page">
<h1>Whoops!</h1>
<p>
We're sorry, an error occurred while loading the page, please go back and try it again.<br />
The site administrators have been informed about this error.
<?php
if(self::getDebug()) {
?>
More information is shown bellow.<br />
<br />
<b>Warning: </b>The error information displayed bellow is sensitive, it's a big security risk to show this information to public.
You can disable this information by disabling the debug mode of Carbon CMS.
You can disable the debug mode at any time by changing the '<i>carbon.debug</i>' setting to '<i>false</i>' in the configuration file.
<?php
} else {
?>
<br /><br />
The debug mode of Carbon CMS is currently disabled. Detailed error information is not being displayed for security reasons.<br />
You can enable the debug mode of Carbon CMS at any time by changing the '<i>carbon.debug</i>' setting to '<i>true</i>' in the configuration file.<br />
Please note it's a high security risk to show debug information to public.
<?php
}
?>
</p>
</div>
<?php
// Make sure it'statements allowed to show sensitive data
if(self::getDebug()) {
// Get the exception type in proper format
$ex_type = get_class($ex);
if(strrpos($ex_type, '\\'))
$ex_type = '<span style="color: #666;">' . substr($ex_type, 0, strrpos($ex_type, '\\') + 1) . '</span>' . substr($ex_type, strrpos($ex_type, '\\') + 1);
// Show the error information
?>
<div id="page">
<h1>Error Information</h1>
<table>
<tr><td>Type:</td><td><?=$ex_type; ?></td></tr>
<tr><td>Message:</td><td><?=$ex->getMessage(); ?></td></tr>
<tr><td>File:</td><td><span class="file"><?=$ex->getFile(); ?></span></td></tr>
<tr><td>Code:</td><td><?=$ex->getCode(); ?></td></tr>
</table>
<?php
// Show possible solutions, if available
if($ex instanceof CarbonException) {
if($ex->hasSolutions()) {
?>
<h1>Possible Solutions</h1>
<p>
<ul><li><?=implode('</li><li>', $ex->getSolutions()); ?></li></ul>
</p>
<?php
}
}
// Show the trace of the exception
self::showTrace($ex);
}
// Print the bottom of the page
self::printPageBottom();
// End the current request
exit();
// TODO: Return false?
}
/**
* Print the top of the error page
* @param string $page_title Page title to use
*/
private static function printPageTop($page_title) {
$site_path = '';
if(Core::getConfig() != null)
$site_path = Core::getConfig()->getValue('general', 'site_path', $site_path);
// TODO: Make sure the correct stylesheet is being used
?>
<html>
<head>
<title>Carbon CMS - <?=$page_title; ?></title>
<link rel="stylesheet" type="text/css" href="<?=$site_path; ?>/style/error.css">
</head>
<body>
<div id="page-wrap">
<?php
}
/**
* Print the bottom of the error page
*/
private static function printPageBottom() {
// TODO: Show a proper footer with the correct version info
?>
</div>
</div>
<div id="footer-wrap">
<div class="footer-left">
<a href="http://timvisee.com/" title="About Carbon CMS" target="_new" >Carbon CMS</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;Version <?=CARBON_CMS_VERSION_NAME; ?>
</div>
<div class="footer-right">
Carbon CMS by <a href="http://timvisee.com/" title="About Tim Vis&eacute;e" target="_new" >Tim Vis&eacute;e</a>
</div>
</div>
</body>
</html>
<?php
}
/**
* Shows the trace of the error/exception
* @param \Exception $ex Exception instance
*/
public static function showTrace($ex) {
// Get the trace of the exception
$trace = $ex->getTrace();
$start = 0;
if(get_class($ex) === "ErrorException")
$start++;
// Make sure the first trace step isn't being shown twice
if($ex->getFile() === $trace[$start]['set_file'] && $ex->getLine() === $trace[$start]['line'])
$start++;
// Print the top of the error trace
?>
<h1>Error Trace</h1>
<div id="trace">
<?php
// Show the first trace step (the Exception itself)
self::showTraceStep('Source', null, null, null, null, $ex->getLine(), $ex->getFile());
// Show a message if any trace is skipped
if($start == 1)
echo '<i style="color: #666;">Skipped 1 identical trace...</i><br /><br />';
else if($start > 1)
echo '<i style="color: #666;">Skipped ' . $start . ' identical traces...</i><br /><br />';
// Put each trace step on the page
for($i = $start; $i < count($trace); $i++) {
// Get the information about the current trace step
$t_class = @$trace[$i]['class'];
$t_type = @$trace[$i]['type'];
$t_function = @$trace[$i]['function'];
if(isset($trace[$i]['line']))
$t_line = $trace[$i]['line'];
else
$t_line = @$trace[$i]['args'][3];
if(isset($trace[$i]['set_file']))
$t_file = $trace[$i]['set_file'];
else
$t_file = @$trace[$i]['args'][2];
$t_args = @$trace[$i]['args'];
// Show the trace step
self::showTraceStep($i + 1, $t_class, $t_type, $t_function, $t_args, $t_line, $t_file);
}
?>
</div>
<?php
// Show the error context (if available) for PHP errors
if(isset($trace[0]['args']) && is_array($trace[0]['args'][4])) {
// Print the header
?>
<h1>Error Context</h1>
<?php
// Show the context
self::showContext($trace[0]['args'][4]);
}
}
/**
* Show a trace step
* @param mixed|null $t_id Trace identifier or index, or null to hide the trace identifer
* @param string $t_class Trace class
* @param string $t_type Trace type
* @param string $t_function Trace function
* @param array|null $t_args Trace arguments, null for no arguments (default: null)
* @param int|null $t_line Trace line, null if the line is unknown (default: null)
* @param string|null $t_file Trace set_file, null if the set_file is unknown (default: null)
*/
public static function showTraceStep($t_id, $t_class, $t_type, $t_function, $t_args = null, $t_line = null, $t_file = null) {
// Get the proper function name
if($t_function != null) {
if($t_class != null && $t_type != null)
$func = $t_class . $t_type . $t_function . '(' . self::joinArguments($t_args) . ');';
else
$func = $t_function . '(' . self::joinArguments($t_args) . ');';
} else {
if($t_file != null && $t_line != null) {
if(!file_exists($t_file))
return;
$file_contents = file($t_file);
if(!isset($file_contents[$t_line - 1]))
return;
$func = trim($file_contents[$t_line - 1]);
} else
return;
}
?>
<div class="step">
<h2>
<?php
// Print the trace index if set
if($t_id !== null)
echo $t_id . ': ';
// Print the trace function
?>
<?=$func; ?></h2>
<table>
<tr><td>Function:</td><td><span class="function"><?=self::highlight($func); ?></span></td></tr>
<?php
// Print the line
if($t_line != null)
echo '<tr><td>Line:</td><td>' . $t_line . '</td></tr>';
else
echo '<tr><td>Line:</td><td><i>Unknown</i></td></tr>';
// Print the set_file
if($t_file != null)
echo '<tr><td>File:</td><td><span class="set_file">' . $t_file . '</span></td></tr>';
else
echo '<tr><td>File:</td><td><i>Unknown</i></td></tr>';
// Print the code, if the set_file and line are known
if($t_line != null && $t_file != null)
echo '<tr><td>Code:</td><td style="padding-top: 4px;">' . self::getCode($t_file, $t_line) . '</td></tr>';
?>
</table>
</div>
<?php
}
/**
* Joins function arguments to they can be displayed properly
* @param array $args $args Arguments to join
* @return string Joined arguments as HTML
*/
public static function joinArguments($args) {
if(!is_array($args))
return '';
$out = '';
$sep = '';
foreach($args as $arg) {
if(is_numeric($arg))
$out .= $sep . $arg;
else if(is_string($arg))
$out .= $sep . '"' . $arg . '"';
else if(is_bool($arg)) {
if($arg)
$out .= $sep . 'true';
else
$out .= $sep . 'false';
} else if(is_object($arg))
$out .= $sep . get_class($arg);
else if(is_array($arg))
$out .= $sep . 'Array(' . count($arg) . ')';
else
$out .= $sep . $arg;
$sep = ', ';
}
// Return the $out contents
return $out;
}
/**
* Get the code of a set_file to display
* @param string $file File to display the code of
* @param int $line Line to show the code of
* @return string Code frame as HTML
*/
public static function getCode($file, $line) {
// Read the set_file
$lines = file($file);
$out = '';
$out .= '<div id="code">';
$out .= '<div class="lines">';
// Add the line numbers
for($i = $line - 5; $i < $line + 4; $i++) {
if(isset($lines[$i])) {
if($i + 1 != $line)
$out .= '<code>' . ($i + 1) . '</code><br />';
else
$out .= '<code style="font-weight: bold;">' . ($i + 1) . '</code><br />';
}
}
$out .= '</div>';
$out .= '<div class="code">';
// Show the set_file lines
for($i = $line - 5; $i < $line + 4; $i++) {
if(isset($lines[$i])) {
if($i + 1 != $line)
$out .= self::highlight($lines[$i]);
else
$out .= '<span style="background: yellow; width: 100%; font-weight: bold; display: block;">' . self::highlight($lines[$i]) . '</span>';
}
}
$out .= '</div>';
$out .= '</div>';
return $out;
}
/**
* Highlight PHP code in HTML format
* @param string $str String to highlight
* @return string Highlighted string
*/
public static function highlight($str) {
// Check if this line starts with the PHP opening tag, if not highlight the text
if(strpos($str, '<?php') !== false)
return highlight_string($str, true);
// Highlight the PHP opening tag
return preg_replace('/&lt;\?php&nbsp;/', '', highlight_string('<?php ' . $str, true));
}
/**
* Show the context of a trace step
* @param array $context Trace context
*/
public static function showContext($context) {
?>
<div id="code">
<div class="code">
<?php
$i = 0;
foreach($context as $name => $value) {
// If the item is not the first item, a line break should be added
if($i > 0)
echo '<br /><br />';
ob_start();
echo '$' . $name . ' = ';
self::printVariable($value);
echo ';';
$code = ob_get_clean();
echo self::highlight($code);
$i++;
}
?>
</div>
</div>
<?php
}
/**
* Print a variable as HTML
* @param mixed $value Variable value
* @param int $tabs Amount of tabs to intent (default: 2)
*/
public static function printVariable($value, $tabs = 2) {
if(is_numeric($value)) {
// Print a numeric value
echo $value;
} elseif(is_bool($value)) {
// Render a boolean value
if ($value)
echo 'true';
else
echo 'false';
} elseif(is_string($value)) {
// Render a string value
// TODO: Should use a sanatize method here?
echo '"' . htmlspecialchars($value) . '"';
} elseif(is_array($value)) {
// Render an array
echo 'Array(';
if(empty($value)) {
echo ")";
return;
}
// Check whether the array is associative
if(ArrayUtils::isAssoc($value)) {
$first = true;
foreach($value as $key => $val) {
if(!$first) {
echo ",";
$first = false;
}
echo "\n";
echo str_pad('', ($tabs + 1) * 4);
printf("\"%statements\" => ", $key);
self::printVariable($val, $tabs + 1);
}
} else {
// Ordinary array
$first = true;
foreach($value as $val) {
if(!$first) {
echo ",";
$first = false;
}
print "\n";
echo str_pad('',($tabs + 1) * 4);
self::printVariable($val, $tabs + 1);
}
}
echo "\n";
echo str_pad('', ($tabs) * 4);
echo ")";
} elseif(is_object($value)) {
// Render an object
$vars = get_object_vars($value);
if (count($vars) === 0) {
echo get_class($value) . '()';
return;
}
echo get_class($value) . "(\n";
foreach(get_object_vars($value) as $key => $val) {
echo str_pad('', ($tabs + 1) * 4);
printf("$%statements = ", $key);
self::printVariable($val, $tabs + 1);
echo ";\n";
}
echo ")";
} else {
// Unsupported variable type, print the plain variable
echo $value;
}
}
/**
* Destroy and unregister the error handler
* @param bool $restore_error_handler [optional] False to keep the error handler registered
* @param bool $restore_exception_handler [optional] False to keep the exception handler registered
*/
public static function destroy($restore_error_handler = true, $restore_exception_handler = true) {
// Restore the error and exception handlers
if($restore_error_handler)
restore_error_handler();
if($restore_exception_handler)
restore_exception_handler();
}
}
<?php
/**
* CancellableEvent.php
* CancellableEvent class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class CancellableEvent extends Event {
private $isCanceled = false;
/**
* Check if the event was canceled
* @return boolean True if canceled
*/
public function isCanceled() {
return $this->isCanceled;
}
/**
* Set if the event should be canceled
* @param boolean $isCanceled True to cancel
*/
public function setCaceled($isCanceled) {
$this->isCanceled = $isCanceled;
}
}
<?php
/**
* Event.php
* Event class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
abstract class Event {
/**
* Constructor
*/
public function __construct() { }
/**
* Get the event name
* @return string Event name
*/
public function getEventName() {
return get_class($this);
}
}
<?php
/**
* Listener.php
* Listener class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Listener { }
<?php
/**
* PluginDisableEvent.php
* Module Disable Event class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event\plugin;
use carbon\core\event\Event;
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class PluginDisableEvent extends Event {
private $plugin;
/**
* Constructor
*/
public function __construct(\carbon\core\module\Module $plugin) {
$this->plugin = $plugin;
}
/**
* Get the plugin being disabled
* @return Module Module being disabled
*/
public function getPlugin() {
return $this->plugin;
}
}
<?php
/**
* PluginEnableEvent.php
* Module Enable Event class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event\plugin;
use carbon\core\event\CancellableEvent;
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class PluginEnableEvent extends CancellableEvent {
private $plugin;
/**
* Constructor
*/
public function __construct(Module $plugin) {
$this->plugin = $plugin;
}
/**
* Get the plugin being enabled
*
*@return \carbon\core\module\Module Module being enabled
*/
public function getPlugin() {
return $this->plugin;
}
}
<?php
/**
* PluginLoadEvent.php
* Module Load Event class of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\event\plugin;
use carbon\core\event\CancellableEvent;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class PluginLoadEvent extends CancellableEvent {
private $plugin_name;
/**
* Constructor
*/
public function __construct($plugin_name) {
$this->plugin_name = $plugin_name;
}
/**
* Get the plugin name being loaded
* @return string Module name being loaded
*/
public function getPluginName() {
return $this->plugin_name;
}
}
<?php
/**
* EventManager.php
*
* The EventManager class manages all the events registered by plugins.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\event\Event;
use carbon\core\event\Listener;
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Manages all the events registered by plugins.
* @package core
* @author Tim Visee
*/
class EventManager {
private $registered_events = array();
/**
* Constructor
*/
public function __construct() { }
/**
* Register events from an event listener
*
*@param Listener $l Event listener
* @param \carbon\core\module\Module $p Module to register the events for
*/
public function registerEvents(Listener $l, Module $p) {
// Make sure any of the parameters is not null
if($l == null || $p == null)
return;
// Get the event class and it'statements methods
$event_class = new \ReflectionClass($l);
$event_methods = $event_class->getMethods();
// Loop through each method and check if it could be used as event listener, if so register the function
foreach($event_methods as $method) {
// Make sure the method has one parameter
if(sizeof($method->getParameters()) != 1)
continue;
// The method has to be public (or it can'elapsed be called!)
if(!$method->isPublic())
continue;
// Get the parameter name and class of the current method
$param_arr = $method->getParameters();
$param = $param_arr[0];
$param_class = $param->getClass()->getName();
// Check if the class of the parameter is an instance of an Event
if($this->isEvent($param_class)) {
// Get the method/function name and the name of the event
$event_name = $param_class;
$function_name = $method->getName();
// TODO: Make sure the function is not already registered!
// Register the event
array_push($this->registered_events,
new RegisteredEvent($event_name, $p, $l, $function_name)
);
}
}
}
/**
* Get all registered events
* @return array Array of registered events, empty array when no event was registered yet
*/
public function getRegisteredEvents() {
return $this->registered_events;
}
/**
* Get the amount of registered events
* @return int Amount of registered events
*/
public function getRegisteredEventsCount() {
return sizeof($this->registered_events);
}
/**
* Unregister all events
* @return int Amount of unregistered events
*/
public function unregisterEvents() {
$unregistered_amount = sizeof($this->registered_events);
$this->registered_events = Array();
return $unregistered_amount;
}
// TODO: Method unregisterEventsFromPlugin(); not tested yet!
/**
* Unregister all events registered by a specific plugin
*
*@param \carbon\core\module\Module $p Module to unregister the events from
*/
public function unregisterEventsFromPlugin(Module $p) {
// Loop through each registered event
for($i = 0; $i < sizeof($this->registered_events); $i++) {
// Get the RegisteredEvent instance of the current entry
$e = $this->registered_events[$i];
// Does the plugin of the current entry equal to the param plugin
if($e->getPlugin()->equals($p)) {
// Unregister the current entry
array_splice($this->registered_events, $i, 1);
$i--;
}
}
}
/**
* Call an event
*
*@param Event $event Event to call
* @param \carbon\core\module\Module $plugin Module to call event too, null to call to every enabled plugin (optional)
*/
public function callEvent(Event $event, Module $plugin = null) {
// Get the name of the event
$event_name = $event->getEventName();
// Loop through each registered event to check if it should be called
foreach($this->registered_events as $entry) {
// Make sure the name of the current event equals to the event name that should be called
if($event_name == $entry->getEventName()) {
// Only call this registered event if the plugin equals to $plugin, or if $plugin equals to null
if($entry->getPlugin()->equals($plugin) || $plugin == null)
$entry->callFunction($event);
}
}
}
/**
* Check if an event has been registered
* @param Listener $listener Event listener
* @param string $function_name Function name
* @param Module $plugin Module
* @return boolean True if this event was registered
*/
public function isEventRegistered(Listener $listener, string $function_name, Module $plugin) {
// Loop through each registered event to check if it equals to the method parameters
foreach($this->registered_events as $entry) {
// Check if the listener equals
if($entry->getListener() == $listener) {
// Check if the function name equals
if($entry->getFunctionName() == $function_name) {
// Check if the plugin equals
return ($entry->getPlugin()->equals($plugin));
}
}
}
}
/**
* Check if a EventName corresponds to an event
* @param string $event_name Event name to check
* @return boolean True if event
*/
public function isEvent($event_name) {
// TODO: 'core\event\Event' and NOT just 'Event' because namespaces are being used!
// TODO: Use something like getClass() from the Event plugin
return is_subclass_of($event_name, 'lib\event\Event');
}
}
class RegisteredEvent {
private $event_name;
private $plugin;
private $listener;
private $function_name;
/**
* Constructor
*
*@param string $event_name Event name
* @param \carbon\core\module\Module $plugin Module of the registered event
* @param Listener $listener The event listener
* @param string $function_name The name of the function to call once the event is being called
*/
public function __construct($event_name, Module $plugin, Listener $listener, $function_name) {
$this->event_name = $event_name;
$this->plugin = $plugin;
$this->listener = $listener;
$this->function_name = $function_name;
}
/**
* Get the name of the event
* @return string Event name
*/
public function getEventName() {
return $this->event_name;
}
/**
* Get the plugin of the event
*
* @return \carbon\core\module\Module Module
*/
public function getPlugin() {
return $this->plugin;
}
/**
* Get the listener for the event
* @return Listener Listener for the event
*/
public function getListener() {
return $this->listener;
}
/**
* Get the function name that will be called once the event is being called
* @return string Function name that will be called
*/
public function getFunctionName() {
return $this->function_name;
}
/**
* Call the function
*/
public function callFunction(Event $event) {
// Get the listener and the function name
$listener = $this->listener;
$function = $this->function_name;
// Make sure the event name is from the same type
if($this->getEventName() != $event->getEventName()) {
throw new Exception('Carbon CMS: Event was \'' . $event->getEventName() . '\' tried to be called as \'' . $this->getEventName() . '\', call canceled!');
}
// Call the event
$listener->$function($event);
}
}
<?php
/**
* CarbonCoreException.php
*
* Main Carbon CMS Core Exception.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception;
use carbon\core\exception\CarbonException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonCoreException class
* @package core\exception
* @author Tim Visee
*/
class CarbonCoreException extends CarbonException { }
<?php
/**
* CarbonException.php
*
* Main Carbon CMS Exception.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonException class
* @package core\exception
* @author Tim Visee
*/
class CarbonException extends \Exception {
/** @var array|null $solutions Array with possible solutions for this exception, or null */
private $solutions = null;
/**
* Constructor
* @param string $message [optional] Exception message
* @param int $code [optional] Exception code
* @param \Exception $previous [optional] Previous chained exception
* @param string|array|null $solutions [optional] $solution String or searrayith possible solutions
*/
public function __construct($message = "", $code = 0, \Exception $previous = null, $solutions = null) {
// Store the possible solution
$this->setSolutions($solutions);
// Construct the parent
parent::__construct($message, $code, $previous);
}
/**
* Check whether this exception has any possible solutions included
* @return bool True if this exception has any possible solutions included
*/
public function hasSolutions() {
// Make sure the solutions doesn'elapsed equal to null
if($this->solutions == null)
return false;
// The solutions must be an array
return (is_array($this->solutions));
}
/**
* Get an array containing all possible solutions for this exception
* @return array Array containing all possible solutions for this exception. May be an empty array.
*/
public function getSolutions() {
if(is_array($this->solutions))
return $this->solutions;
return Array();
}
/**
* Set the possible solutions for this exception
* @param string|array|null $solutions String or array with possible solutions,
* null to clear the list of solutions
*/
public function setSolutions($solutions = null) {
if($solutions != null) {
if(is_array($solutions))
$this->solutions = $solutions;
else if(is_string($solutions))
$this->solutions = Array($solutions);
} else
$solutions = null;
}
}
<?php
/**
* CarbonConfigException.php
*
* Carbon CMS Config Exception class set_file
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\config;
use carbon\core\exception\CarbonCoreException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonConfigException class
* @package core\exception
* @author Tim Visee
*/
class CarbonConfigException extends CarbonCoreException { }
<?php
/**
* CarbonConfigLoadingException.php
*
* Carbon CMS Config Load Exception class set_file
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\config;
use carbon\core\exception\config\CarbonConfigException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonConfigLoadException class
* @package core\exception
* @author Tim Visee
*/
class CarbonConfigLoadException extends CarbonConfigException { }
<?php
/**
* CarbonUnknownLocaleException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\language\CarbonLocaleException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonUnknownLocaleException class
* @package core\exception
* @author Tim Visee
*/
class CarbonInvalidLocaleException extends CarbonLocaleException { }
<?php
/**
* CarbonLanguageException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\CarbonCoreException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLanguageException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLanguageException extends CarbonCoreException { }
<?php
/**
* CarbonLanguageFileException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\language\CarbonLanguageException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLanguageFileException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLanguageFileException extends CarbonLanguageException { }
<?php
/**
* CarbonLanguageFileLoadException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\language\CarbonLanguageFileException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLanguageFileLoadException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLanguageFileLoadException extends CarbonLanguageFileException { }
<?php
/**
* CarbonLocaleException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\CarbonCoreException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLocaleException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLocaleException extends CarbonCoreException { }
<?php
/**
* CarbonLocalesLoadException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\language\CarbonLocaleException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLocalesLoadException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLocalesLoadException extends CarbonLocaleException { }
<?php
/**
* CarbonUnknownLanguageException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language;
use carbon\core\exception\language\CarbonLanguageException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonUnknownLanguageException class
* @package core\exception
* @author Tim Visee
*/
class CarbonUnknownLanguageException extends CarbonLanguageException { }
<?php
/**
* CarbonLanguageManifestException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language\manifest;
use carbon\core\exception\language\CarbonLanguageException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLanguageManifestException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLanguageManifestException extends CarbonLanguageException {
private $manifest_file;
/**
* Constructor
* @param string $message [optional] Exception message
* @param int $code [optional] Exception code
* @param \Exception $previous [optional] Previous chained exception
* @param string|array|null $solutions [optional] $solution String or array with possible solutions
* @param string|null $manifest_file Path to the manifest set_file which couldn'elapsed be loaded
*/
public function __construct($message = "", $code = 0, \Exception $previous = null, $solutions = null, $manifest_file = null) {
// Store the manifest set_file
$this->manifest_file = $manifest_file;
// Construct the parent
parent::__construct($message, $code, $previous, $solutions);
}
/**
* Get the path to the manifest set_file that couldn'elapsed be loaded
* @return null|string Path to the manifest set_file, might return null if no set_file was set.
*/
public function getManifestFile() {
return $this->manifest_file;
}
}
<?php
/**
* CarbonLanguageManifestLoadException.php
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\language\manifest;
use carbon\core\exception\language\manifest\CarbonLanguageManifestException;
use carbon\core\exception\language\manifest;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonLanguageManifestLoadException class
* @package core\exception
* @author Tim Visee
*/
class CarbonLanguageManifestLoadException extends manifest\CarbonLanguageManifestException { }
<?php
/**
* CarbonRegistryException.php
*
* Carbon CMS Registry Exception class set_file
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core\exception\registry;
use carbon\core\exception\CarbonCoreException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* CarbonRegistryException class
* @package core\exception
* @author Tim Visee
*/
class CarbonRegistryException extends CarbonCoreException { }
<?php
/**
* Form.php
* Form class for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
// TODO: FINISH CLASS!
namespace carbon\core;
use carbon\core\Form\Val;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Steps:
* - Fill out a form
* - POST to PHP
* - Sanatize
* - Validate data
* - Return data
* - Write to Database
*/
class Form {
/**
* @var array $_postData Stores the posted data
*/
private $_postData = array();
/**
* @var object $_val The validator object
*/
private $_val = array();
/**
* Constructor
*/
public function __construct() {
$this->_val = new Val();
}
/**
* Get values from $_POST
* @param string $fieldName Field name
* @return Form this
*/
public function post($fieldName) {
// Add the variable to the $_postData array
if(isset($_POST[$fieldName]))
$this->_postData[$fieldName] = $_POST[$fieldName];
else
$this->_postData[$fieldName] = null;
return $this;
}
/**
* Return posted data
* @param string $fieldName Field name
* @return mixed String, array or null
*/
public function fetch($fieldName = null) {
// Fetch all data, or a single field
if($fieldName == null || trim($fieldName) == '')
return $this->_postData;
else
if(isset($this->_postData[$fieldName]))
return $this->_postData[$fieldName];
else
return null;
}
/**
* Validate variables
* @param string $fieldName Field name to check
* @param string $validatorType Validator type
* @param mixed $arg Validator arguments
* @return boolean true if valid
*/
public function val($fieldName, $validatorType, $arg = null) {
// Get the post item
$postItem = $this->_postData[$fieldName];
// Validate the post item
return $this->_val->{$validatorType}($postItem, $arg);
}
}
?>
<?php
/**
* Val.php
* Val class for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\Form;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Val {
/**
* Constructor
*/
public function __construct() { }
/**
* Validate an EMail address
* @param string $email EMail address
* @return boolean true if valid
*/
public function email($email) {
return (bool) preg_match(';^([a-z0-9-_]+)(.[a-z0-9-_]+)*@([a-z0-9-]+)(.[a-z0-9-]+)*.[a-z]{2,4}$;i', mysql_escape_string($email));
}
/**
* Validate the minimum length of a string
* @param string $data String
* @param int $minLength Minimum string length
* @return boolean true if valid
*/
public function minLength($data, $minLength) {
return (strlen($data) >= $minLength);
}
/**
* Validate the maximum length of a string
* @param string $data String
* @param int $maxLength Maximum string length
* @return boolean true if valid
*/
public function maxLength($data, $maxLength) {
return (strlen($data) <= $maxLength);
}
/**
* Validate the length of a string
* @param string $data String
* @param int $length String length
* @return boolean true if valid
*/
public function length($data, $length) {
return (strlen($data) == $length);
}
}
?>
<?php
/**
* Hash.php
*
* The Hash class is used to hash data.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Hash class
* @package core
* @author Tim Visee
*/
class Hash {
/**
* Hash data
* @param string $data The data to encode
* @param string $algo (Optional) The algorithm to use, null to use the default algorithm
* @param string $salt (Optional) The salt to use, null to use the default salt
* @return string The hashed data
*/
public static function hash($data, $algo = null, $salt = null) {
// Get the config instance
$cfg = Core::getConfig();
// If the $algo param is not set, get the default value from the config set_file
if($algo == null)
$algo = $cfg->getValue('hash', 'hash_algorithm');
// If the $salt param was not set, get the default value from the config set_file
if($salt == null)
$salt = $cfg->getValue('hash', 'hash_key');
// Hash the data
$context = hash_init($algo, HASH_HMAC, $salt);
hash_update($context, $data);
// Return the hashed data
return hash_final($context);
}
}
<?php
// Make sure the current PHP version is supported
if(version_compare(phpversion(), '5.3.1', '<'))
// PHP version the server is running is not supported, show an error message
// TODO: Show proper error message
die('This server is running PHP ' . phpversion() . ', the required PHP version to start Carbon Core is PHP 5.3.1 or higher,
please install PHP 5.3.1 or higher on your server!');
// Prevent direct requests to this file due to security reasons
defined('CARBON_SITE_ROOT') or die('Access denied!');
// Make sure Carbon Core isn't initialized already
if(defined('CARBON_CORE_INIT'))
if(CARBON_CORE_INIT === true)
return;
// Define some Carbon Core constants
/** Defines the root directory for Carbon Core */
define('CARBON_CORE_ROOT', __DIR__);
/** Define the version code of the current installed Carbon Core instance */
define('CARBON_CORE_VERSION_CODE', 1);
/** Define the version name of the current installed Carbon Core instance */
define('CARBON_CORE_VERSION_NAME', '0.1');
// Carbon Core initialized successfully, define the CARBON_CORE_INIT constant to store the initialization state
/** Defines whether Carbon Core is initialized successfully */
define('CARBON_CORE_INIT', true);
<?php
namespace carbon\core\io\filesystem\directory;
use carbon\core\io\filesystem\FilesystemObject;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Directory extends FilesystemObject {
/**
* Get the Directory instance from a FileSystemObject instance or the path a string from a directory.
* If the filesystem object is an existing file or symbolic link, null will be returned.
* The directory or directory path has to be valid.
*
* @param FilesystemObject|string $dir Filesystem object instance or the path of a directory as a string.
*
* @return Directory|null The directory as a Directory instance.
* Or null if the directory couldn't be cast to a Directory instance.
*/
public static function asDirectory($dir) {
return DirectoryHelper::asDirectory($dir);
}
/**
* Get the name of the directory without any path information.
* Alias of {@link FileSystemObject::getBasename()}.
*
* @return string Name of the directory.
*
* @see FileSystemObject::getBasename();
*/
public function getDirectoryName() {
return $this->getBasename();
}
/**
* Attempt to create a directory at the path.
*
* @param int $mode [optional] See the mkdir() function for documentation.
* @param bool $recursive [optional] See the mkdir() function for documentation.
* @param null $context [optional] See the mkdir() function for documentation.
*
* @return Directory|null Directory instance on success, null on failure.
*
* @see mkdir()
*/
public function createDirectory($mode = 0777, $recursive = false, $context = null) {
// Make sure the mode is a valid number, if not, use the default value
if(!is_int($mode))
$mode = 0777;
// Get the directory path
$path = $this->getCanonicalPath();
// Attempt to create the directory, return the result
if(mkdir($path, $mode, $recursive, $context))
return new self($path);
return null;
}
/**
* Delete the contents of the directory.
*
* @param resource $context [optional] See the unlink() function for documentation
*
* @return int Number of deleted files, symbolic links and directories, a negative number will be returned if the
* directory doesn't exist or on failure.
*
* @see unlink()
*/
public function deleteContents($context = null) {
// The directory must exist
if(!$this->isDirectory())
return -1;
// Count the deleted files, symbolic links and directories
$count = 0;
// TODO: Use list methods from the File class if possible!
// Get the directory path
$dirPath = $this->getPath();
if(substr($dirPath, strlen($dirPath) - 1, 1) != '/')
$dirPath .= '/';
// List and delete the files and sub directories
// TODO: List all files using the list() method from the Directory class, instead of glob!
$files = glob($dirPath . '*', GLOB_MARK);
foreach($files as $filePath) {
$file = new FilesystemObject($filePath);
$count += $file->delete($context, true);
}
// Return the number of removed files and directories
return $count;
}
/**
* Get all contents of the directory.
*
* @param bool $recursive [optional] True to read all directory contents recursively.
* @param int $types [optional] The type of filesystem objects to include. Defaults to FLAG_TYPE_ALL which allows all
* filesystem object types. Choose from:
* - FLAG_TYPE_OBJECT
* - FLAG_TYPE_FILE
* - FLAG_TYPE_DIRECTORY
* - FLAG_TYPE_SYMBOLIC_LINK
* - FLAG_TYPE_ALL
* Use null to default to FLAG_TYPE_ALL.
*
* @return array|mixed|null An array with all directory contents, or null on failure.
*
* @see FilesystemObjectFlags
*/
// TODO: Add types parameter!
public function getContents($recursive = false, $types = self::FLAG_TYPE_ALL) {
return DirectoryHelper::getContents($this, $recursive, $types, null);
}
/**
* Check whether the directory is valid. The directory doesn't need to exist.
* The directory may not be an existing file or symbolic link.
*
* @return bool True if the directory seems to be valid, false otherwise.
*/
public function isValid() {
return DirectoryHelper::isValid($this);
}
}
<?php
namespace carbon\core\io\filesystem\directory;
// Prevent direct requests to this set_file due to security reasons
use carbon\core\io\filesystem\file\File;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\io\filesystem\symboliclink\SymbolicLink;
use carbon\core\util\StringUtils;
defined('CARBON_CORE_INIT') or die('Access denied!');
class DirectoryHandler {
// TODO: Shared file handle feature!
/** @var Directory The handled directory. */
private $dir;
/** @var resource Filesystem object handle for the directory */
protected $handle = null;
/**
* Constructor.
*
* @param Directory|string $dir The directory or the directory path as a string of the directory that needs to be
* handled. The directory must exist before the handle can be opened.
*
* @throws \Exception Throws an exception when the directory isn't valid.
*/
public function __construct($dir) {
// Set the handled directory, throw an exception on failure
if(!$this->setDirectory($dir))
throw new \Exception();
// TODO: Throw a custom exception on error!
}
/**
* Destructor, to ensure the directory handle is closed safely before destroying the object.
*/
public function __destruct() {
// Make sure to close the directory handle before destroying the object.
$this->close();
}
/**
* Get the handled directory.
*
* @return Directory Handled directory.
*/
public function getDirectory() {
return $this->dir;
}
/**
* Set the handled directory. Automatically reopens the directory handle if the handle was opened with a different
* directory. The directory must exist before the handle can be opened.
*
* @param Directory|FilesystemObject|string $dir Handled directory, a directory or filesystem object
* or the path of a directory as a string.
*
* @param resource $context [optional] See PHPs opendir() function for more details.
* This directory context will be used if the handle needs to be reopened.
* If set to null, the current directory context will be used.
*
* @return bool True if succeed, false on failure. Also returns true if the directory wasn't changed.
*
* @see opendir();
*/
public function setDirectory($dir, $context = null) {
// Get $dir as Directory instance, return false on failure
if(($dir = Directory::asDirectory($dir)) === null)
return false;
// Make sure the directory changed
if($this->dir === $dir)
return true;
// Set the directory instance, return the result
$this->dir = $dir;
// Reopen the directory handler if it's opened already, return the result
if($this->isOpened())
return $this->reopen($context);
return true;
}
/**
* Open the directory handle. The directory must exist before a handle can be opened.
*
* @param resource $context [optional] See PHPs opendir() function for more details.
*
* @return bool True on success, false on failure. True will be returned if the handle was opened already.
*
* @see opendir();
*/
public function open($context = null) {
// Make sure the directory handle isn't opened already
if($this->isOpened())
return true;
// Make sure the directory is valid and existing
if(!$this->dir->isDirectory())
return false;
// Open the directory handle
// TODO: Improve the code bellow!
if($context === null) {
if(($this->handle = @opendir($this->dir->getPath())) === false) {
// Close the handle again, return false
$this->close();
return false;
}
} else {
if(($this->handle = @opendir($this->dir->getPath(), $context)) === false) {
// Close the handle again, return false
$this->close();
return false;
}
}
// Return the result
return true;
}
/**
* Reopen the currently opened directory handle. The directory handle must be opened in order to reopen it.
*
* @param resource|null $context [optional] See PHPs opendir() function for more details.
* The context of the current opened directory handle will be used if not specified.
*
* @return bool True on success, false on failure. False will also be returned if the directory handle wasn't
* opened.
*
* @see opendir();
*/
public function reopen($context = null) {
// Make sure the directory handle is opened
if(!$this->isOpened())
return false;
// TODO: Store the current context!
// Store the current directory context if not set
if($context === null)
$context = null;
// Close the directory handle, return false on error
if(!$this->close())
return false;
// Reopen the directory handle, return the result
return $this->open($context);
}
/**
* Check whether the directory handle is opened.
*
* @return bool True if the directory handle is opened, false otherwise.
*/
public function isOpened() {
// Check whether $handle is null or false
if($this->handle === null || $this->handle === false)
return false;
// Check whether the handle is a valid resource, return the result
return is_resource($this->handle);
}
/**
* Close the directory handle if it's opened.
*
* @return bool True on success, false on failure. True will also be returned if no handle was opened.
*/
public function close() {
// Make sure the directory handle is opened
if(!$this->isOpened())
return true;
// Close the directory handle
@closedir($this->handle);
// Set $handle to null, return the result
$this->handle = null;
return true;
}
/**
* Get the ID of the PHP resource which is used by the opened directory handle.
* The directory handler must be opened.
*
* @return int|null The ID of the directory handle, null on failure.
*/
public function getHandleId() {
// Make sure the directory handler is opened
if(!$this->isOpened())
return null;
// Return the ID of the handle
return intval($this->handle);
}
/**
* Rewind the directory handle. This will set the directory pointer to the beginning of the directory which causes
* the following directory scans to start from the beginning.
*
* @return bool True if succeed, false if failed because no directory handle was opened.
*/
public function rewind() {
// Make sure a file handle is opened
if(!$this->isOpened())
return false;
// Rewind the file handle, return the result
@rewinddir($this->handle);
return true;
}
/**
* Get the next entry from the directory handle.
*
* @param bool $ignorePeriodDirs [optional] True to ignore period directories such as /. and /.. .
*
* @return File|Directory|SymbolicLink|FilesystemObject|null The next filesystem object from the directory.
*/
public function read($ignorePeriodDirs = true) {
// Make sure a directory handle is opened
if(!$this->isOpened())
return null;
// Read the next entry from the directory handle, and make sure it's valid
// TODO: Should we close the handle when null is returned?
if(($entry = readdir($this->handle)) === false)
return null;
// Process single and double period entries
if(($entry === '.' || $entry == '..') && $ignorePeriodDirs)
return $this->read($ignorePeriodDirs);
// Return period directories
if($entry === '.')
return $this->dir;
else if($entry === '..')
return $this->dir->getParent();
// Create a proper filesystem object instance of the entry, return the result
return FilesystemObject::from($this->dir, $entry);
}
/**
* List files and directories in the directory.
*
* @param int $sortOrder [optional] The order of the entries being returned. Using alphabetical order by default.
* Order types:
* - SCANDIR_SORT_ASCENDING
* - SCANDIR_SORT_DESCENDING
* - SCANDIR_SORT_NONE
* @param resource $context [optional] The directory context. See PHPs scandir() function for more information.
* @param bool $ignorePeriodDirs [optional] True to ignore period dirs such as /. and /.., false otherwise.
*
* @return Array|null A list of filesystem objects as an array or null on failure.
*
* @see scandir();
*/
public function scan($sortOrder = SCANDIR_SORT_ASCENDING, $context, $ignorePeriodDirs = false) {
// Make sure the directory handle was opened
if(!$this->isOpened())
return null;
// Scan the directory
if($context !== null)
$scan = scandir($this->handle, $sortOrder, $context);
else
$scan = scandir($this->handle, $sortOrder);
// Make sure the result was valid
if($scan === false)
return null;
// Create an array of filesystem objects
$entries = Array();
foreach($scan as $entry) {
// Check whether period directories should be ignored
if($ignorePeriodDirs && (StringUtils::equals($entry, Array('.', '..'), false, true)))
continue;
$entries[] = FileSystemObject::from($this->dir, $entry);
}
// Return the result
return $entries;
}
/**
* Get the directory handle.
*
* @return resource Directory handle as resource, null will be returned if the directory handle isn't opened.
*/
public function getHandle() {
// Make sure the directory handle is opened, if not return null
if(!$this->isOpened())
return null;
// Return the directory handle
return $this->handle;
}
/**
* Check whether the directory handler is valid. The directory handler is valid when a valid directory is set.
*
* @return bool True if the directory handler is valid, false otherwise.
*/
public function isValid() {
// Make sure a directory is set
if($this->dir === null)
return false;
// Make sure the directory is valid, return the result
return $this->dir->isValid();
}
}
<?php
namespace carbon\core\io\filesystem\directory;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\io\filesystem\FilesystemObjectHelper;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DirectoryHelper extends FilesystemObjectHelper {
/**
* Get the Directory instance from a FileSystemObject instance or the path a string from a directory.
* If the filesystem object is an existing file or symbolic link, $default will be returned.
* The directory or directory path has to be valid.
*
* @param FilesystemObject|string $dir Filesystem object instance or the path of a directory as a string.
* @param mixed|null $default [optional] Default value returned if the directory couldn't be instantiated,
* possibly because the $dir param was invalid.
*
* @return Directory|mixed The directory as a Directory instance.
* Or the $default param value if the directory couldn't be cast to a Directory instance.
*/
public static function asDirectory($dir, $default = null) {
// Create a new directory instance when $dir is a string, or when $dir is a FileSystemObject instance
// but not a Directory
if(is_string($dir) || ($dir instanceof FilesystemObject && !$dir instanceof Directory))
$dir = new Directory($dir);
// The $dir must be a Directory instance, if not, return the default
if(!$dir instanceof Directory)
return $default;
// Make sure the directory is valid, if not, return the $default value
if(!$dir->isValid())
return $default;
// Return the directory
return $dir;
}
/**
* Delete all the contents of a directory.
*
* @param Directory|string $dir Directory instance or directory path as a string to delete the contents from.
* @param resource $context [optional] See the unlink() function for documentation.
*
* @return int
*
* @see unlink()
*/
public static function deleteContents($dir, $context = null) {
// Convert the directory into a path string, return the $default value if failed
if(($dir = self::asPath($dir, false)) === null)
return -1;
// The directory must exist
if(!self::isDirectory($dir))
return -1;
// Count the deleted files, symbolic links and directories
$count = 0;
// Create a directory scanner, then list and delete all directory contents
$scanner = new DirectoryScanner($dir);
while(($item = $scanner->read()) !== null)
$count += self::delete($item, $context, true);
// Return the number of removed files and directories
return $count;
}
/**
* Get all contents of a directory.
*
* @param Directory|string $dir Directory instance or the directory as a path string to get the contents from.
* @param bool $recursive [optional] True to read all directory contents recursively.
* @param int $types [optional] The type of filesystem objects to include. Defaults to FLAG_TYPE_ALL which allows all
* filesystem object types. Choose from:
* - FLAG_TYPE_OBJECT
* - FLAG_TYPE_FILE
* - FLAG_TYPE_DIRECTORY
* - FLAG_TYPE_SYMBOLIC_LINK
* - FLAG_TYPE_ALL
* Use null to default to FLAG_TYPE_ALL.
* @param mixed|null $default [optional] The default value to return on failure.
*
* @return array|mixed|null An array with all directory contents, or the $default value on failure.
*
* @see FilesystemObjectFlags
*/
// TODO: Better $types description!
public static function getContents($dir, $recursive = false, $types = self::FLAG_TYPE_ALL, $default = null) {
// Convert the directory into a path string, return the $default value if failed
if(($dir = self::asPath($dir, false)) === null)
return $default;
// Make sure the filesystem object is a directory, and exists
if(!self::isDirectory($dir))
return null;
// Create a directory scanner
$scanner = new DirectoryScanner($dir, true);
// Scan the directory and return the result if it's valid
if(($out = $scanner->readAll($recursive, $types)) === null)
return $default;
return $out;
}
/**
* Validate a directory or the path of a directory. The directory doesn't need to exist.
* The directory may not be an existing file or symbolic link.
*
* @param \carbon\core\io\filesystem\FilesystemObject|string $dir Filesystem object instance or the path of a directory as a string.
*
* @return bool True if the directory path seems to be valid, false otherwise.
*/
public static function isValid($dir) {
// Convert the directory into a string, return the false if failed
if(($dir = self::asPath($dir, false)) === null)
return false;
// Make sure the directory is valid as FileSystemObject
if(!FilesystemObjectHelper::isValid($dir))
return false;
// Make sure the directory isn't a file or symbolic link, return the result
return !(FilesystemObjectHelper::isFile($dir) || FilesystemObjectHelper::isSymbolicLink($dir));
}
}
<?php
namespace carbon\core\io\filesystem\directory;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\io\filesystem\file\File;
use carbon\core\io\filesystem\FilesystemObjectFlags;
use carbon\core\io\filesystem\symboliclink\SymbolicLink;
use Exception;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DirectoryScanner implements FilesystemObjectFlags {
/** @var Directory Directory to scan. */
private $dir = null;
/** @var DirectoryHandler Directory handler instance. */
private $handler = null;
/**
* @var File The last successfully scanned directory entry.
* A successfully readAll() scan will cause the last entry from the returned list to be remembered.
*/
private $lastRead = null;
/**
* @var bool True if period directories such as /. and /.. are ignored and skipped while scanning the directory,
* false if they're included.
*/
private $ignorePeriodDirs = true;
// TODO: Automatically close the handler when this object is destroyed
/**
* Constructor.
*
* @param Directory|string $dir Directory instance or a directory path as string to scan.
*
* @throws Exception Throws an exception $dir isn't a directory or doesn't exist.
*/
public function __construct($dir) {
// Set the directory
if(!$this->setDirectory($dir))
// TODO: Throw an exception!
throw new Exception();
}
/**
* Destructor.
* Close the directory handler properly before deconstructing.
*/
public function __destruct() {
// Close the directory handler if it's opened
$this->close();
}
/**
* Get the directory.
*
* @return Directory|null Directory instance. Null will be returned if no directory was set.
*/
public function getDirectory() {
return $this->dir;
}
/**
* Set the directory to scan
*
* @param Directory|string $dir Directory instance or a directory path as string.
*
* @return bool True on success, false on failure.
*/
public function setDirectory($dir) {
// Convert $dir into a Directory instance, return false on failure
if(($dir = DirectoryHelper::asDirectory($dir)) === null)
return false;
// Set the directory
$this->dir = $dir;
// Set the directory in the directory handler if it's initialized, return the result
if($this->handler !== null)
return $this->handler->setDirectory($dir);
return true;
}
/**
* Check whether period directory entries such as /. and /.. are ignored and skipped while scanning the directory.
*
* @return bool True if period directories are ignored and skipped, false if they're included.
*/
public function isIgnorePeriodDirectories() {
return $this->ignorePeriodDirs;
}
/**
* Set whether period directory entries such as /. and /.. should be ignored and skipped while scanning the
* directory.
*
* @param bool $ignorePeriodDirs True to ignore and skip the period directories, false to include them.
*/
public function setIgnorePeriodDirectories($ignorePeriodDirs) {
$this->ignorePeriodDirs = $ignorePeriodDirs;
}
/**
* Open the directory handle. The directory handler is opened automatically as it's required.
*
* @return bool True on success, false on failure. Returns true if the directory handle was opened already.
*/
public function open() {
// Make sure the handler isn't opened already
if($this->isOpened())
return true;
// Open the director handler or instantiate a new one if required, return the result
if($this->handler === null)
$this->handler = new DirectoryHandler($this->dir);
return $this->handler->open();
}
/**
* Check whether the directory handle is opened.
*
* @return bool True if the directory is opened, false otherwise.
*/
public function isOpened() {
// Make sure the handler is instantiated
if($this->handler === null)
return false;
// Check whether the handler is opened, return the result
return $this->handler->isOpened();
}
/**
* Close the currently opened directory handle.
*
* @return bool True on success, false on failure. True will also be returned if no directory handle was opened.
*/
public function close() {
// Make sure the directory handler is opened
if($this->isOpened())
return false;
// CLose the directory handler, return the result
return $this->handler->close();
}
/**
* Rewind the directory handler. The directory handler is opened automatically as it's required.
*
* @return bool True if succeed, false if failed because no directory handle was opened.
*/
public function rewind() {
// Open the directory handler if it isn't opened yet
$this->open();
// Rewind the directory handler, return the result
return $this->handler->rewind();
}
/**
* Get the next entry from the directory handle. The directory handler is opened automatically as it's required.
*
* @return File|Directory|SymbolicLink|FilesystemObject|null The next filesystem object from the directory.
*/
// TODO: Improve the quality of this method
public function read() {
// Open the directory handler if it isn't opened yet
$this->open();
// Read from the directory handler, and store the last read directory
if(($read = $this->handler->read($this->ignorePeriodDirs)) === null)
return null;
// Set the last read entry and return the result
$this->lastRead = $read;
return $read;
}
/**
* Get the successfully read directory entry from this directory scanner. Returns null if no entry was read yet.
*
* @return File|Directory|SymbolicLink|FilesystemObject|null The last successfully read directory entry.
*/
public function getLastRead() {
return $this->lastRead;
}
/**
* Read all next entries from the directory handle, starting after the last entry that was read.
* The directory handler is opened automatically as it's required. The directory handle will be rewind automatically
* after calling this method.
*
* @param bool $recursive True to scan the directory recursively.
* @param int $types [optional] The type of filesystem objects to read. Defaults to FLAG_TYPE_ALL which allows all
* filesystem object types. Choose from:
* - FLAG_TYPE_OBJECT
* - FLAG_TYPE_FILE
* - FLAG_TYPE_DIRECTORY
* - FLAG_TYPE_SYMBOLIC_LINK
* - FLAG_TYPE_ALL
* Use null to default to FLAG_TYPE_ALL.
*
* @return Array|null An array of all directory entries, or null on failure.
*
* @see FilesystemObjectFlags
*/
public function readAll($recursive = false, $types = self::FLAG_TYPE_ALL) {
// Default to FLAG_TYPE_ALL if $types is set to null
if($types === null)
$types = self::FLAG_TYPE_ALL;
// Open the directory handler if it isn't opened yet
$this->open();
// Create an array to store all filesystem objects in
$entries = Array();
// Read all entries form the directory handle
while(($entry = $this->read($this->ignorePeriodDirs)) !== null) {
// Check this filesystem object type should be included
if($entry instanceof File) {
if(!($types & self::FLAG_TYPE_FILE))
continue;
} elseif($entry instanceof Directory) {
if(!($types & self::FLAG_TYPE_DIRECTORY))
continue;
} elseif($entry instanceof Directory) {
if(!($types & self::FLAG_TYPE_SYMBOLIC_LINK))
continue;
} elseif(!($types & self::FLAG_TYPE_OBJECT))
continue;
// Add the entry to the list
$entries[] = $entry;
// Check whether the scan is recursive and check whether the current object is a directory
if($recursive && $entry->isDirectory()) {
// Create a new scanner to scan the current object recursively
$scanner = new self($entry);
// Scan all directory objects, make sure the result is valid
if(($items = $scanner->readAll($recursive, $types, $this->ignorePeriodDirs)) === null)
return null;
// Add the objects to the list
$entries = array_merge($entries, $items);
}
}
// Rewind the directory handler
$this->rewind();
// Return the list of filesystem objects
return $entries;
}
/**
* Count the number of directory entries in this directory.
* Period directories such as /. and /.. are ignored if $ignorePeriodDirs is set to true.
* Warning: This method might be resource expensive.
*
* @param resource $context [optional] The directory context. See the DirectoryHandler::scan() method for more information.
*
* @return int The number of entries in this directory, or -1 on failure.
*
* @see DirectoryHandler::scan();
*/
public function count($context = null) {
// Open the directory handler if it isn't opened yet
$this->open();
// Scan the directory, return -1 on failure
if(($entries = $this->handler->scan(SCANDIR_SORT_NONE, $context, $this->ignorePeriodDirs)) === null)
return -1;
// Count and return the directory entries
return count($entries);
}
/**
* Check whether the directory is valid
*
* @return bool True if the directory is valid, false otherwise
*/
public function isValid() {
// Make sure the directory is set
if($this->dir === null)
return false;
// Check whether the directory is valid, return the result
return $this->dir->isValid();
}
}
<?php
namespace carbon\core\io\filesystem\file\accessmode;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FileAccessMode {
/** @var bool True when the file should be readable, false otherwise. */
private $read = false;
/** @var bool True if the file should be writable, false otherwise. */
private $write = false;
/** @var bool True if the file should be created if it doesn't exist, false otherwise. */
private $create = false;
/** @var bool True if the file is forced to be created or an error will be returned, false otherwise. */
private $forceCreate = false;
/** @var bool True to truncate the file once it's opened, false otherwise. */
private $truncate = false;
/** @var bool True to put the file pointer at the ending of the file. */
private $end = false;
/**
* @var bool True if text-mode translation should be enabled to translate \n to \r\n when working with files, false
* to use the regular mode. This feature is only available on Windows based platforms, and is ignored on other
* unsupported platforms.
*/
private $textModeTranslation = false;
/**
* @var bool True to enable the binary mode, false otherwise. It's strongly recommended to enable the binary flag
* when working with binary files to prevent strange problems from occurring with new line characters and images.
*/
private $binary = false;
/**
* Constructor.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $write [optional] True if the file should be writeable, false otherwise.
* @param bool $create [optional] True if the file should be created if it doesn't exist.
* @param bool $forceCreate [optional] True if the file is forced to be created.
* @param bool $truncate [optional] True if the file should be truncated once it's opened.
* @param bool $end [optional] True if the file pointer should be placed at the end of the file.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*/
public function __construct($read = false, $write = false, $create = false, $forceCreate = false, $truncate = false,
$end = false, $textModeTranslation = false, $binary = false) {
// Set the variables
$this->read = $read;
$this->write = $write;
$this->create = $create;
$this->forceCreate = $forceCreate;
$this->truncate = $truncate;
$this->end = $end;
$this->textModeTranslation = $textModeTranslation;
$this->binary = $binary;
}
/**
* Get a file access mode instance. This method will try to instantiate a file access mode instance based on the
* input. This method won't verify the validity of the file access mode string.
*
* @param FileAccessMode|string $mode File access mode instance, or a PHPs file access mode string.
*
* @return FileAccessMode|null The file access mode instance, or null on failure.
*/
public static function asFileAccessMode($mode) {
// Return $mode if it's an FileAccessMode instance
if($mode instanceof FileAccessMode)
return $mode;
// The $mode must be a string
if(!is_string($mode))
return null;
// Trim $mode from unwanted whitespaces
$mode = trim($mode);
// Make sure $mode isn't empty and has a maximum of 4 characters.
if(empty($mode) || strlen($mode) > 4)
return null;
// Try to convert the file access mode string into an instance, return the result
return FileAccessModeFactory::createFromMode($mode);
}
/**
* Check whether the file should be readable.
*
* @return bool True if the file should be readable, false otherwise.
*/
public function isRead() {
return $this->read;
}
/**
* Set whether the file should be readable.
*
* @param bool $readable True if the file should be readable, false otherwise.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setRead($readable) {
// Set whether the file should be readable
$this->read = $readable;
// Return this instance
return $this;
}
/**
* Check whether the file should be writable.
*
* @return bool True if the file should be writable, false otherwise.
*/
public function isWrite() {
return $this->write;
}
/**
* Set whether the file should be writable.
*
* @param bool $writable True if the file should be writable, false otherwise.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setWrite($writable) {
// Set whether the file should be writable
$this->write = $writable;
// Return this instance
return $this;
}
/**
* Check whether the file should be created if it doesn't exist.
*
* @return bool True if the file should be created if it doesn't exist.
*/
public function isCreating() {
return $this->create;
}
/**
* Set whether the file should be craeted if it doesn't exist.
*
* @param bool $creating True if the file should be created if it doesn't exist.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setCreate($creating) {
// Set whether the file should be created
$this->create = $creating;
// Return this instance
return $this;
}
/**
* Check if the file is forced to be created, or else an error will occur.
*
* @return bool True if the file is forced to be created.
*/
public function isForceCreate() {
return $this->forceCreate;
}
/**
* Set whether the file is forced to be created, or else an error will occur.
*
* @param bool $forceCreating True if the file is forced to be created.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setForceCreate($forceCreating) {
// Set whether the file is force to be created
$this->forceCreate = $forceCreating;
// Return this instance
return $this;
}
/**
* Check whether the file should be truncated once it's opened.
*
* @return bool True if the file should be truncated once it's opened.
*/
public function isTruncate() {
return $this->truncate;
}
/**
* Set whether the file should be truncated once it's opened.
*
* @param bool $truncating True if the file should be truncated once it's opened.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setTruncate($truncating) {
// Set whether the file should be truncated
$this->truncate = $truncating;
// Return this instance
return $this;
}
/**
* Check whether the file pointer should be placed at the end of the file.
*
* @return bool True if the file pointer should be placed at the end of the file.
*/
public function isEnd() {
return $this->end;
}
/**
* Set whether the file pointer should be placed at the end of the file.
*
* @param bool $end True if the file pointer should be placed at the end of the file.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setEnd($end) {
// Set whether the file pointer should be put at the end of the file
$this->end = $end;
// Return this instance
return $this;
}
/**
* Check whether text-mode translations are enabled to translate \n to \r\n when working with files, false to use
* the regular mode. This feature is only available on Windows based platforms, and is ignored on other unsupported
* platforms.
*
* @return bool True if text-mode translations should be enabled, false to use the regular mode.
*/
public function isTextModeTranslation() {
return $this->textModeTranslation;
}
/**
* Set whether text-mode translations are enabled to translate \n to \r\n when working with files, false to use the
* regular mode. This feature is only available on Windows based platforms, and is ignored on other unsupported
* platforms.
*
* @param bool $textModeTranslation True if text-mode translations should be enabled, false to use the regular mode.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setTextModeTranslation($textModeTranslation) {
// Set whether text-mode translations should be enabled
$this->textModeTranslation = $textModeTranslation;
// Return this instance
return $this;
}
/**
* Check whether the binary mode is used. It's strongly recommended to enable the binary flag when working with
* binary files to prevent strange problems from occurring with new line characters and images.
*
* @return bool True to enable the binary mode, false otherwise.
*/
public function isBinary() {
return $this->binary;
}
/**
* Set whether the binary mode is used. It's strongly recommended to enable the binary flag when working with
* binary files to prevent strange problems from occurring with new line characters and images.
*
* @param bool $binary True to enable the binary mode, false otherwise.
*
* @return FileAccessMode This instance, for method chaining.
*/
public function setBinary($binary) {
// Set the binary mode
$this->binary = $binary;
// Return this instance
return $this;
}
/**
* Get the file access mode for PHPs file methods as a string based on the file access mode properties.
* Some file access mode combinations aren't possible, in that case the most similar mode will be returned.
*
* @return string The file access mode.
*/
public function getMode() {
// TODO: Cache!
// Get the file access mode suffix
$suffix = '';
if($this->textModeTranslation)
$suffix .= 't';
if($this->binary)
$suffix .= 'b';
// Check whether the file should be opened for reading only
if($this->read && !$this->write)
return 'r' . $suffix;
// Check whether the file should be opened for writing only
if(!$this->read && $this->write) {
// Check whether the file should be force created
if($this->forceCreate)
return 'x' . $suffix;
// Check whether the file should be truncated
if($this->truncate)
return 'w' . $suffix;
// Check whether the file should be opened on the end
if($this->end)
return 'a' . $suffix;
// Return the default file access mode for write-only operations
return 'c' . $suffix;
}
// The file should be opened for reading and writing
// Check whether the file should be force created
if($this->forceCreate)
return 'x+' . $suffix;
// Check whether the file should be truncated
if($this->truncate)
return 'w+' . $suffix;
// Check whether the file should be opened on the end
if($this->end)
return 'a+' . $suffix;
// Check whether the file should be created
if($this->create)
return 'c+' . $suffix;
// Return the default file access mode for read and write operations
return 'r+' . $suffix;
}
/**
* Compare the file access mode with an other file access mode instance.
*
* @param FileAccessMode|string $other The other file access mode instance to compare this instance to. A file
* access mode string may be supplied which causes this method to compare the two file access modes as strings.
* @param bool $exact [optional] True to ensure the two instances exactly equal each other with all of their
* properties, false to just compare the file access mode strings of both instances since different instances may
* share the same file access mode string. This option is only available if $other was a FileAccessMode instance.
*
* @return bool True if the two instances equal, false otherwise. False will also be returned if the other instance
* is invalid.
*/
public function equals($other, $exact = false) {
// Make sure $other isn't null
if($other === null)
return false;
// Directly compare the two instances
if($this === $other)
return true;
// Compare the two instances as a string, if $other is a string.
if(is_string($other))
return StringUtils::equals($this->getMode(), $other, false, true);
// Compare the PHPs file access mode strings if the comparison doesn't have to be exact.
if(!$exact)
return StringUtils::equals($this->getMode(), $other->getMode(), false);
// The two instances doesn't seem to equal, return false
return false;
}
/**
* Convert the file access mode to a string. This will return the getMode() value.
*
* @return string The file access mode as a string.
*
* @see FileAccessMode::getMode();
*/
public function __toString() {
return $this->getMode();
}
}
<?php
namespace carbon\core\io\filesystem\file\accessmode;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FileAccessModeFactory {
/**
* Create a new file access mode instance.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $write [optional] True if the file should be writeable, false otherwise.
* @param bool $create [optional] True if the file should be created if it doesn't exist.
* @param bool $forceCreate [optional] True if the file is forced to be created.
* @param bool $truncate [optional] True if the file should be truncated once it's opened.
* @param bool $end [optional] True if the file pointer should be placed at the end of the file.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance, for method chaining.
*/
public static function create($read = false, $write = false, $create = false, $forceCreate = false,
$truncate = false, $end = false, $textModeTranslation = false, $binary = false) {
return new self($read, $write, $create, $forceCreate, $truncate, $end, $textModeTranslation, $binary);
}
/**
* Create a file access mode instance based on a PHP file access modes string.
* This method won't verify the validity of the file access mode string.
*
* @param string $mode PHPs file access mode string.
*
* @return FileAccessMode|null The file access mode instance, or null on failure.
*/
public static function createFromMode($mode) {
// Return $mode if it's an FileAccessMode instance
if($mode instanceof FileAccessMode)
return $mode;
// Create and return a new file access mode instance
return new FileAccessMode(
// Check whether the file access mode should be readable
StringUtils::contains($mode, '+'),
// Check whether the file access mode should create the file
!StringUtils::contains($mode, 'r') || StringUtils::contains($mode, '+'),
// Check whether the file access mode should create the file
!StringUtils::contains($mode, 'r'),
// Check whether the file access mode should force create the file
StringUtils::contains($mode, 'x'),
// Check whether the file access mode truncates the file
StringUtils::contains($mode, 'w'),
// Check whether the file access mode should start at the end of the file
StringUtils::contains($mode, 'a'),
// Check whether the file access mode should use text-mode translations on supported platforms
StringUtils::contains($mode, 't'),
// Check whether the file access mode should use binary mode
StringUtils::contains($mode, 'b')
);
}
/**
* Create the file access mode which could be used for reading only.
*
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance.
*/
public static function createReadOnlyMode($textModeTranslation = false, $binary = false) {
return new FileAccessMode(true, false, false, false, false, false, $textModeTranslation, $binary);
}
/**
* Create the file access mode which forces a new file to be created.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance.
*/
public static function createForceCreateMode($read = true, $textModeTranslation = false, $binary = false) {
return new FileAccessMode($read, true, false, true, false, false, $textModeTranslation, $binary);
}
/**
* Create the file access mode which truncates the file.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance.
*/
public static function createTruncateMode($read = true, $textModeTranslation = false, $binary = false) {
return new FileAccessMode($read, true, false, false, true, false, $textModeTranslation, $binary);
}
/**
* Create the file access mode which places the file pointer at the end of the file, and tries to create the file if it
* doesn't exist.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance.
*/
public static function createAppendMode($read = true, $textModeTranslation = false, $binary = false) {
return new FileAccessMode($read, true, true, false, false, true, $textModeTranslation, $binary);
}
/**
* Create the file access mode which places the file pointer at the beginning of the file, and tries to create the
* file if it doesn't exist.
*
* @param bool $read [optional] True if the file should be readable, false otherwise.
* @param bool $textModeTranslation [optional] True to enable text-mode translations to translate \n to \r\n on
* supported platforms.
* @param bool $binary True to enable binary mode.
*
* @return FileAccessMode File access mode instance.
*/
public static function createPrependMode($read = true, $textModeTranslation = false, $binary = false) {
return new FileAccessMode($read, true, true, false, false, false, $textModeTranslation, $binary);
}
}
<?php
/**
* File.php
* The File class, which is used to manage files in the filesystem.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2014. All rights reserved.
*/
namespace carbon\core\io\filesystem\file;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\io\filesystem\permissions\SystemGroup;
use carbon\core\io\filesystem\permissions\SystemUser;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* File class. This class extends all the features of the FileSystemObject class.
* This class references to an file in the filesystem based on it's path.
* This class could be used to manage files in the filesystem.
*
* @package carbon\core\io\filesystem\file
* @author Tim Visee
*/
class File extends FilesystemObject {
/**
* Get the File instance from a FileSystemObject instance or the path a string from a file.
* If the filesystem object is an existing directory or symbolic link, null will be returned.
* The file or file path has to be valid.
*
* @param \carbon\core\io\filesystem\FilesystemObject|string $file Filesystem object instance or the path of a file as a string.
*
* @return File|null The file as a File instance. Or null if the file couldn't be cast to a File instance.
*/
public static function asFile($file) {
return FileHelper::asFile($file, null);
}
/**
* Create the file if it doesn't exist.
*
* @return bool True if the file was created, false otherwise.
* False will also be returned if the file already existed.
*/
public function createFile() {
return FileHelper::createFile($this);
}
/**
* Get the name of the file with it's extension without any path information.
* Alias of {@see FileSystemObject::getBasename()}
*
* @return string Name of the file and it's extension.
*
* @see FileSystemObject::getBasename();
*/
public function getFileName() {
return $this->getBasename();
}
/**
* Get the extension of a file. File names ending with a period, do have an extension.
*
* @param bool $withPeriod True to include the period with the returned value, false to exclude the period.
*
* @return string|null The file extension as a string.
* Null will be returned on failure or if the file doesn't have an extension
*/
public function getExtension($withPeriod = false) {
return FileHelper::getExtension($this, $withPeriod);
}
/**
* Check whether the file has an extension. File names ending with a period, do have an extension.
*
* @return bool True if the file has an extension, false otherwise. False will also be returned on failure.
*/
public function hasExtension() {
return FileHelper::hasExtension($this);
}
/**
* Get the file owner.
*
* @return SystemUser|null Owner of the file, or null on failure.
*/
public function getOwner() {
return FileHelper::getOwner($this);
}
/**
* Get the file group.
*
* @return SystemGroup|null Group of the file, or null on failure.
*/
public function getGroup() {
return FileHelper::getGroup($this);
}
/**
* Get the size in bytes of the file.
*
* @return int|mixed|null File size in bytes, or null on failure.
*/
public function getSize() {
return FileHelper::getSize($this);
}
/**
* Get the last access time of the file as a unix timestamp.
*
* @return int|null Last access time of the file as a unix timestamp, or null on failure.
*/
public function getLastAccessTime() {
return FileHelper::getLastAccessTime($this);
}
/**
* Get the inode change time of the file as unix timestamp.
*
* @return int|null Inode change time of the file as unix timestamp, or null on failure.
*/
public function getChangeTime() {
return FileHelper::getChangeTime($this);
}
/**
* Get the last modification time of the file as a unix timestamp.
*
* @return int|null Last modification time of the file as unix timestamp, or null on failure.
*/
public function getModificationTime() {
return FileHelper::getModificationTime($this);
}
/**
* Touch the file and set the modification and action time.
*
* @param int|null $time [optional] The modification time to set as a timestamp. Null to use the current time.
* @param int|null $accessTime [optional] The access time to set as a timestamp. Null to use the $time value.
*
* @return bool True on success, false on failure.
*/
public function touch($time = null, $accessTime = null) {
return FileHelper::touchFile($this, $time, $accessTime);
}
/**
* Get the file contents of the file.
*
* @param resource $context [optional] See PHPs fopen() function for more details.
* @param int|null $offset [optional] The offset of the file pointer to start reading from measured in bytes from
* the beginning of the file, or null to ignore this parameter.
* @param int|null $maxLength [optional] The maximum number of bytes to read from the file, or null to ignore this
* parameter.
*
* @return string|null File contents as a string, or null on failure.
*/
public function getContents($context = null, $offset = null, $maxLength = null) {
return FileHelper::getContents($this, $context, $offset, $maxLength, null);
}
/**
* Put contents into the file. This will truncate the file if it exists already.
* If the file doesn't exist, it will be created.
*
* @param string $data The data to put into the file.
* @param resource $context [optional] See PHPs fopen() function for more details.
*
* @return int|null The number of written bytes, or null on failure.
*
* @see fopen();
*/
public function putContents($data, $context = null) {
return FileHelper::putContents($data, $context);
}
/**
* Append to the file.
*
* @param string $data The data to append to the file.
* @param resource $context [optional] See PHPs fopen() function for more details.
* @param bool $create [optional] True to attempt to create the file if it doens't exist.
*
* @return int|null The amount of bytes appended to the file.
*/
public function append($data, $context = null, $create = true) {
return FileHelper::append($this, $data, $context, $create);
}
/**
* Create a file handler for this file.
*
* @return FileHandler File handler instance.
*/
public function createHandler() {
return new FileHandler($this);
}
/**
* Check whether the file is valid. The file doesn't need to exist.
* The file may not be an existing directory or symbolic link.
*
* @return bool True if the file seems to be valid, false otherwise.
*/
public function isValid() {
return FileHelper::isValid($this);
}
// TODO: Method to require and require_once a file.
// TODO: To prepend contents to a file.
}
<?php
namespace carbon\core\io\filesystem\file;
class FileArrayHelper {
/**
* Check whether the files in an array of files exist.
*
* @param $files
* @param $all
*/
public static function exist($files, $all) {
}
}
<?php
namespace carbon\core\io\filesystem\file;
use carbon\core\io\filesystem\file\accessmode\FileAccessMode;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FileHandler {
// TODO: Shared file handle feature!
/** @var File The handled file. */
private $file;
/** @var resource Filesystem object handle of the file */
protected $handle = null;
/** @var FileAccessMode The file access mode used by the handler. */
private $fileMode = null;
/**
* Constructor.
*
* @param File|string $file The file or the file path as a string of the file that needs to be handled.
*
* @throws \Exception Throws an exception when the file isn't valid.
*/
public function __construct($file) {
// Set the handled file, throw an exception on failure
if(!$this->setFile($file))
throw new \Exception();
// TODO: Throw a custom exception on error!
}
/**
* Destructor, to ensure the file handle is closed safely before destroying the object.
*/
public function __destruct() {
// Make sure to close the file handle before destroying the object.
$this->close();
}
/**
* Get the handled file.
*
* @return File Handled file.
*/
public function getFile() {
return $this->file;
}
/**
* Set the handled file. Automatically reopens the file handle if the handle was opened with a different file.
*
* @param File|\carbon\core\io\filesystem\FilesystemObject|string $file Handled file, a file or filesystem object
* or the path of a file as a string.
* @param FileAccessMode|string $mode [optional] The file access mode used by the handler as file access mode
* instance or as PHPs file access mode string. See PHPs fopen() function for more details. This file access mode
* will be used if the handle needs to be reopened. If set to null, the current file access mode will be used.
* @param resource $context [optional] See PHPs fopen() function for more details.
* This file context will be used if the handle needs to be reopened.
* If set to null, the current file context will be used.
*
* @return bool True if succeed, false on failure. Also returns true if the file wasn't changed.
*
* @see fopen();
*/
public function setFile($file, $mode = null, $context = null) {
// Get $file as File instance and $mode as FileAccessMode instance, return false on failure
if(($file = File::asFile($file)) === null)
return false;
$mode = FileAccessMode::asFileAccessMode($mode);
// Make sure the file changed
if($this->file === $file)
return true;
// Set the file instance, return the result
$this->file = $file;
// Reopen the file handler if it's opened already, return the result
if($this->isOpened())
return $this->reopen($mode->getMode(), $context);
return true;
}
/**
* Open the file handle.
*
* @param FileAccessMode|string $mode [optional] The file access mode used by the handler as file access mode
* instance or as PHPs file access mode string. See PHPs fopen() function for more details.
* @param resource $context [optional] See PHPs fopen() function for more details.
*
* @return bool True on success, false on failure. True will be returned if the handle was opened already.
*
* @see fopen();
*/
public function open($mode, $context = null) {
// Make sure the file handle isn't opened already
if($this->isOpened())
return true;
// Convert $mode into a FileAccessMode instance, return false on failure
if(($mode = FileAccessMode::asFileAccessMode($mode)) === null)
return false;
// Open the file handle
// TODO: Improve the code bellow!
if($context === null) {
if(($this->handle = @fopen($this->file->getPath(), $mode->getMode(), false)) === false) {
// Close the handle again, return false
$this->close();
return false;
}
} else {
if(($this->handle = @fopen($this->file->getPath(), $mode->getMode(), false, $context)) === false) {
// Close the handle again, return false
$this->close();
return false;
}
}
// Set the file mode used
$this->fileMode = $mode;
// Return the result
return true;
}
/**
* Reopen the currently opened file handle. The file handle must be opened in order to reopen it.
*
* @param FileAccessMode|string $mode [optional] The file access mode used by the handler as file access mode
* instance or as PHPs file access mode string. See PHPs fopen() function for more details. This file access mode
* will be used if the handle needs to be reopened. If set to null, the current file access mode will be used.
* @param resource|null $context [optional] See PHPs fopen() function for more details.
* The context of the current opened file handle will be used if not specified.
*
* @return bool True on success, false on failure. False will also be returned if the file handle wasn't opened.
*
* @see fopen();
*/
public function reopen($mode = null, $context = null) {
// Make sure the file handle is opened
if(!$this->isOpened())
return false;
// Try to convert $mode into a FileAccessMode instance if set
$mode = FileAccessMode::asFileAccessMode($mode);
// TODO: Store the current context!
// Store the current file mode and context if not set
if($mode === null)
$mode = $this->fileMode;
if($context === null)
$context = null;
// Close the file handle, return false on error
if(!$this->close())
return false;
// Reopen the file handle, return the result
return $this->open($mode, $context);
}
/**
* Check whether the file handle is opened.
*
* @return bool True if the file handle is opened, false otherwise.
*/
public function isOpened() {
// Check whether $handle is null or false
if($this->handle === null || $this->handle === false)
return false;
// Check whether the handle is a valid resource, return the result
return is_resource($this->handle);
}
/**
* Close the file handle if it's opened.
*
* @return bool True on success, false on failure. True will also be returned if no handle was opened.
*/
public function close() {
// Make sure the file handle is opened
if(!$this->isOpened())
return true;
// Close the file handle, return false on failure
if(@fclose($this->handle) === false) {
// Set $handle to null, return the result
$this->handle = null;
return false;
}
// Set the file access mode
$this->fileMode = null;
// Set $handle to null, return the result
$this->handle = null;
return true;
}
/**
* Get the file access mode which is used by the currently opened file handle.
*
* @return FileAccessMode|null The file access mode instance which is used by the currently opened file handle.
* Null will be returned if no file handle was opened.
*/
public function getFileAccessMode() {
// Check whether the file handle is closed, if so return null
if(!$this->isOpened())
return null;
// Return the used file access mode
return $this->fileMode;
}
/**
* Rewind the file handle.
*
* @return bool True if succeed, false if failed because no file handle was opened.
*/
public function rewind() {
// Make sure a file handle is opened
if(!$this->isOpened())
return false;
// Rewind the file handle, return the result
return rewind($this->handle);
}
/**
* Read a line from the file starting at the file pointer. The file handle must be opened.
* Reading ends when the maximum number of bytes is read, if specified by $length.
* Reading ends when a new line is reached (the new line character is included in the output).
* Reading ends when the end of the file is reached.
* This method is binary safe.
*
* @param int|null $length [optional] Specifies the maximum number of bytes to read,
* null to disable a maximum length.
*
* @return string|null The read data, or null if an error occurred.
*/
public function getLine($length = null) {
// Read the line, return null if an error occurs
if(($out = fgets($this->handle, $length)) === false)
return null;
// Return the read data
return $out;
}
/**
* Read a line from the file starting at the file pointer. The file handle must be opened.
* This method attempts to strip HTML, PHP tags and NUL bytes.
* Reading ends when the maximum number of bytes is read, if specified by $length.
* Reading ends when a new line is reached (the new line character is included in the output).
* Reading ends when the end of the file is reached.
* This method is binary safe.
*
* @param int|null $length [optional] Specifies the maximum number of bytes to read,
* null to disable a maximum length.
* @param string $allowTags [optional] A list of allowed tags, which shouldn't be stripped by this method.
* Whitespaces aren't allowed, and tags are case-insensitive.
*
* @return string|null The read data, or null if the end of the file was reached or if an error occurred.
*/
public function getLineStripped($length = null, $allowTags = '') {
// Read the line with stripped tags, return null if an error occurs
if(($out = fgetss($this->handle, $length, $allowTags)) === false)
return null;
// Return the read data
return $out;
}
/**
* Read the next character from the file starting at the file pointer. The file handle must be opened.
* This method is binary safe.
*
* @return string|null The read character, or null if the end of the file was reached or if an error occurred.
*/
public function getChar() {
// Read the character, return null if an error occurs
if(($out = fgetc($this->handle)) === false)
return null;
// Return the read character
return $out;
}
/**
* Read the contents of the file starting at the file pointer. The file handle must be opened.
* Reading ends when the maximum number of bytes is read, specified by $length.
* Reading ends when the end of the file is reached.
* Reading ends when the stream is read buffered and it does not represent a plain file,
* at most one read of up to a number of bytes equal to the chunk size (usually 8192) is made;
* depending on the previously buffered data, the size of the returned data may be larger than the chunk size.
* This method is binary safe.
*
* @param int $length [optional] Specifies the maximum number of bytes to read. This value must be an integer which
* equals or is greater than zero.
*
* @return string|null The read data, or null if the end of the file was reached or if an error occurred.
*/
public function read($length) {
// Make sure the length is valid
if(!is_int($length) || $length < 0)
return null;
// Read the data, return null if an error occurs
if(($out = fread($this->handle, $length)) === false)
return null;
// Return the read data
return $out;
}
/**
* Write a string to a file starting at the file pointer. The file handle must be opened.
* Writing ends when the end of the string is reached, or when the number of written bytes equals $length.
* This method is binary safe.
*
* @param string $str The string to write to the file.
* @param int|null $length [optional] The maximum number of bytes to write, or null to ignore a maximum length.
*
* @return int|null The number of written bytes, or null on failure.
*/
public function write($str, $length = null) {
// Check whether $length is specified
if($length !== null) {
// Write the string to the file, return null if an error occurs
if(($out = fwrite($this->handle, $str, $length)) === false)
return null;
} else
// Write the string to the file, return null if an error occurs
if(($out = fwrite($this->handle, $str)) === false)
return null;
// Return the number of written bytes
return $out;
}
/**
* Allows file locking and unlocking. Files are automatically unlocked when {@see close()} is called.
*
* @param int $operation Operation type. You can use:
* - LOCK_SH to acquire a shared lock (reader).
* - LOCK_EX to acquire an exclusive lock (writer).
* - LOCK_UN to release a lock (shared or exclusive).
* It is also possible to add LOCK_NB as a bitmask to any of the above operations if you don't want to block while
* locking, this is not supported on Windows.
* @param int|null $wouldBlock [optional] This argument is set to 1 if the lock would block
* (EWOULDBLOCK errno condition), this is not supported on Windows.
*
* @return bool True on success, false on failure.
*/
public function lock($operation, $wouldBlock = null) {
return flock($this->handle, $operation, $wouldBlock);
}
/**
* Set the file position indicator. The file handle must be opened.
* The new position of the indicator is measured in bytes from the beginning of the file.
* This method moves the indicator to the $whence position with $offset added.
*
* @param int $offset The offset.
* To move to a position before the end-of-file, you need to pass a negative value in $offset and set
* $whence to SEEK_END.
* @param int $whence Positioning origin, choose from:
* SEEK_SET: Set position equal to offset bytes.
* SEEK_CUR: Set position to current location plus offset.
* SEEK_END: Set position to end-of-file plus offset.
*
* @return bool True on success, false on failure.
*/
public function seek($offset, $whence = SEEK_SET) {
return fseek($this->handle, $offset, $whence) === 0;
}
/**
* Passes through the file starting at the file pointer until the end of the file is reached.
* The file handle must be opened. This method is binary safe.
*
* @return int|null The number of passed characters, or null if the end of the file was reached or if an error occurred.
*/
public function passthru() {
// Passthru the data, return null if an error occurs
if(($count = fpassthru($this->handle)) === false)
return null;
// Return the number of passed bytes
return $count;
}
/**
* Truncate the file to a given length.
*
* @param int $size [optional] The size to truncate to measured in bytes.<br>
* If $size is larger than the file then the file is extended with null bytes.<br>
* If $size is smaller than the file then the file is truncated to that size.
*
* @return bool True on success, false on failure.
*/
public function truncate($size = 0) {
// Truncate the file, return the result
return ftruncate($this->handle, $size);
}
/**
* Get the file handle.
*
* @return resource File handle as resource, null will be returned if the file handle isn't opened.
*/
public function getHandle() {
// Make sure the file handler is opened, if not return null
if(!$this->isOpened())
return null;
// Return the file handle
return $this->handle;
}
/**
* Get the ID of the PHP resource which is used by the opened file handle. The file handler must be opened.
*
* @return int|null The ID of the file handle, null on failure.
*/
public function getHandleId() {
// Make sure the file handler is opened
if(!$this->isOpened())
return null;
// Return the ID of the handle
return intval($this->handle);
}
/**
* Check whether the file handler is valid. The file handler is valid if a valid file is set.
*
* @return bool True if the file handler is valid, false otherwise.
*/
public function isValid() {
// Make sure a file is set
if($this->file === null)
return false;
// Check whether the file is valid, return the result
return $this->file->isValid();
}
}
<?php
namespace carbon\core\io\filesystem\file;
use carbon\core\io\filesystem\file\accessmode\FileAccessModeFactory;
use carbon\core\io\filesystem\FilesystemObject;
use carbon\core\io\filesystem\FilesystemObjectHelper;
use carbon\core\io\filesystem\permissions\SystemGroup;
use carbon\core\io\filesystem\permissions\SystemUser;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FileHelper extends FilesystemObjectHelper {
// TODO: Only allow $default to be returned on functions getting data!
/**
* Get the File instance from a FileSystemObject instance or the path a string from a file.
* If the filesystem object is an existing directory or symbolic link, $default will be returned.
* The file or file path has to be valid.
*
* @param \carbon\core\io\filesystem\FilesystemObject|string $file Filesystem object instance or the path of a file as a string.
* @param mixed|null $default [optional] Default value returned if the file couldn't be instantiated,
* possibly because the $file param was invalid.
*
* @return File|mixed The file as a File instance.
* Or the $default param value if the file couldn't be cast to a File instance.
*/
public static function asFile($file, $default = null) {
// Create a new file instance when $file is a string, or when $file is a FileSystemObject instance
// but not a File
if(is_string($file) || ($file instanceof FilesystemObject && !$file instanceof File))
$file = new File($file);
// The $file must be a file instance, if not, return the default
if(!$file instanceof File)
return $default;
// Make sure the file is valid, if not, return the $default value
if(!$file->isValid())
return $default;
// Return the file
return $file;
}
/**
* Create a file if it doesn't exist.
*
* @param File|string $file File instance or a file path as a string of the file which should be created.
*
* @return bool True if the file was created, false otherwise.
* False will also be returned if the file already existed.
*/
public static function createFile($file) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return false;
// Make sure the file doesn't exist
if(self::exists($file))
return false;
// TODO: Create the file recursively, for non existing directories!
// Create and open the file, return false on error
$handler = new FileHandler($file);
if(!$handler->open('w'))
return false;
// Close the file handle again, return the result
return $handler->close();
}
/**
* Get the extension of a file. File names ending with a period, do have an extension.
*
* @param File|string $file File instance or file path as a string to get the extension from.
* @param bool $withPeriod [optional] True to include the period with the returned value, false to exclude the period.
* @param mixed|null $default [optional] The default value to return on failure.
*
* @return string|mixed|null The file extension as a string. The $default value will be returned on failure.
*/
public static function getExtension($file, $withPeriod = false, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Get the path extension info
$info = pathinfo($file);
// Return the extension, with or without a prefix period, or $default if the file doesn't have an extension.
if(isset($info['extension']))
return ($withPeriod ? '.' : '') . $info['extension'];
return $default;
}
/**
* Check whether the file has an extension. File names ending with a period, do have an extension.
*
* @param File|string $file File instance or file path as a string.
*
* @return bool True if the file has an extension, false otherwise. False will also be returned on failure.
*/
public static function hasExtension($file) {
return is_string(self::getExtension($file));
}
/**
* Get the file owner.
*
* @param File|string $file File instance or file path as a string to get the owner from.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return SystemUser|null Owner of the file, or null on failure.
*/
public static function getOwner($file, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Get the owners user ID, and make sure it's valid
if(($uid = fileowner($file)) === false)
return $default;
// Return the owner
return new SystemUser($uid);
}
/**
* Get the file group.
*
* @param File|string $file File instance or file path as a string to get the group from.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return SystemGroup|null Group of the file, or null on failure.
*/
public static function getGroup($file, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Make sure the group ID is valid, and make sure it's valid
if(($gid = filegroup($file)) === false)
return $default;
// Return the group
return new SystemGroup($gid);
}
/**
* Get the size in bytes of a file.
*
* @param File|string $file File instance or file path as a string to get the size from.
* @param mixed|null $default [optional] Default value returned on failure.
*
* @return int|mixed|null File size in bytes, or the default value on failure.
*/
public static function getSize($file, $default = -1) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Get and return the file size, if it's valid
if(($size = filesize($file)) === false)
return $default;
return $size;
}
/**
* Get the last access time of the file as a unix timestamp.
*
* @param File|string $file File instance or file path as a string to get the last access time from.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|null Last access time of the file as a unix timestamp, or null on failure.
*/
public static function getLastAccessTime($file, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// TODO: Return as Time instance?
// Get and return the last access time, return $default on failure
if(($time = fileatime($file)) === false)
return $default;
return $time;
}
/**
* Get the inode change time of the file as unix timestamp.
*
* @param File|string $file File instance or file path as a string to get the change time from.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|null Inode change time of the file as unix timestamp, or null on failure.
*/
public static function getChangeTime($file, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// TODO: Return as Time instance?
// Get and return the inode change time, return $default on failure
if(($time = filectime($file)) === false)
return $default;
return $time;
}
/**
* Get the last modification time of the file as a unix timestamp.
*
* @param File|string $file File instance or file path as a string to get the modification time from.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return int|null Last modification time of the file as unix timestamp, or null on failure.
*/
public static function getModificationTime($file, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Get and return the last modification time, return $default on failure
if(($time = filemtime($file)) === false)
return $default;
return $time;
}
/**
* Touch a file and set the modification and access time.
*
* @param File|string $file File instance or file path as a string to touch.
* @param int|null $time [optional] The modification time to set as a timestamp. Null to use the current time.
* @param int|null $accessTime [optional] The access time to set as a timestamp. Null to use the $time value.
*
* @return bool True on success, false on failure.
*/
public static function touchFile($file, $time = null, $accessTime = null) {
// Convert the file into a path string, return false on failure
if(($file = self::asPath($file, false)) === null)
return false;
// Touch the file, return the result
return touch($file, $time, $accessTime);
}
/**
* Get the contents of a file.
*
* @param File|string $file File instance or a file path as string to get the contents from.
* @param resource $context [optional] See PHPs fopen() function for more details.
* @param int|null $offset [optional] The offset of the file pointer to start reading from measured in bytes from
* the beginning of the file, or null to ignore this parameter.
* @param int|null $maxLength [optional] The maximum number of bytes to read from the file, or null to ignore this
* parameter.
* @param mixed|null $default [optional] The default value to return on failure.
*
* @return string|mixed|null The contents of the file, or $default on failure.
*
* @see fopen();
*/
public static function getContents($file, $context = null, $offset = null, $maxLength = null, $default = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return $default;
// Make sure the filesystem object is a file and exists
if(!self::isFile($file))
return $default;
// Open the file with the file reader
$reader = new FileReader($file);
if(!$reader->open(null, $context))
return $default;
// Seek to the proper offset
if(!$reader->seek($offset))
return $default;
// Read the file and return the result
if(($contents = $reader->read($maxLength)) === null)
return $default;
return $contents;
}
/**
* Put contents into a file. This will truncate the file if it exists already.
* If the file doesn't exist, it will be created.
*
* @param File|string $file File instance or a file path as string to put the contents into.
* @param string $data The data to put into the file.
* @param resource $context [optional] See PHPs fopen() function for more details.
*
* @return int|null The number of written bytes, or null on failure.
*
* @see fopen();
*/
public static function putContents($file, $data, $context = null) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return null;
// Make sure the path is valid
if(!self::isValid($file))
return null;
// Open a file writer to write to the file
$writer = new FileWriter($file);
if(!$writer->open(FileAccessModeFactory::createTruncateMode(false), $context))
return null;
// Put the contents into the file, return the result
return $writer->write($data);
}
/**
* Append to a file.
*
* @param File|string $file File instance or a file path as string to append to.
* @param string $data The data to append to the file.
* @param resource $context [optional] See PHPs fopen() function for more details.
* @param bool $create [optional] True to attempt to create the file if it doens't exist.
*
* @return int|null The amount of bytes appended to the file.
*/
public static function append($file, $data, $context = null, $create = true) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file, false)) === null)
return null;
// Make sure the file is valid. If the file shouldn't be created make sure it exists already.
if($create) {
if(!self::isValid($file))
return null;
} else
if(!self::isFile($file))
return null;
// Open the file with the file writer
$writer = new FileWriter($file, FileAccessModeFactory::createAppendMode(false));
if(!$writer->open(null, $context))
return null;
// Append to the file, return the result
return $writer->write($data);
}
/*
/**
* Prepend to a file.
*
* @param File|string $file File instance or a file path as string to prepend to.
* @param string $data The data to prepend to the file.
* @param resource $context [optional] See PHPs fopen() function for more details.
* @param bool $create [optional] True to attempt to create the file if it doens't exist.
*
* @return int|null The amount of bytes prepended to the file.
* /
// TODO: Method is overwriting current content at the beginning of the file, fix this!
public static function prepend($file, $data, $context = null, $create = true) {
// Convert the file into a path string, return the $default value if failed
if(($file = self::asPath($file)) === null)
return null;
// Make sure the filesystem object isn't an existing directory or symbolic link. If the file shouldn't be
// created if it doesn't exist, make sure the object is a file and exists
if($create) {
if(self::isDirectory($file) || self::isSymbolicLink($file))
return null;
} else
if(!self::isFile($file))
return null;
// Open the file with the file writer
$writer = new FileWriter($file, FileAccessModeFactory::createPrependMode(false));
if(!$writer->open(null, $context))
return null;
// Prepend to the file, return the result
return $writer->write($data);
}*/
/**
* Get the correct new line or end of line character that should be used for the current platform.
*
* @return string Correct new line character
*/
// TODO: Should we keep this method?
// TODO: Should we move this method?
public static function getNewLineChar() {
return PHP_EOL;
}
/**
* Validate a file instance or the path of a file. The file doesn't need to exist.
* The file may not be an existing directory or symbolic link.
*
* @param \carbon\core\io\filesystem\FilesystemObject|string $path Filesystem object instance or the path of a file as a string.
*
* @return bool True if the file path seems to be valid, false otherwise.
*/
public static function isValid($path) {
// Convert the file into a string, return the false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Make sure the file is valid as FileSystemObject
if(!FilesystemObjectHelper::isValid($path))
return false;
// Make sure the file isn't a directory or symbolic link, return the result
return !(FilesystemObjectHelper::isDirectory($path) || FilesystemObjectHelper::isSymbolicLink($path));
}
}
<?php
namespace carbon\core\io\filesystem\file;
use carbon\core\io\filesystem\file\accessmode\FileAccessMode;
use carbon\core\io\filesystem\file\accessmode\FileAccessModeFactory;
use carbon\core\io\filesystem\FilesystemObject;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FileReader {
/** @var File File to write to */
private $file = null;
/** @var FileHandler File handler */
private $handler = null;
/** @var FileAccessMode File access mode used to access the file. */
private $fileMode;
// TODO: Ability to set context, instead of just the context in the open() method.
/**
* Constructor.
*
* @param FilesystemObject|string $file File instance or the path of a file as a string of the file to read from.
* The file or file path must be an existing file.
*
* @throws \Exception Throws an exception on error.
*/
public function __construct($file) {
// Set the default file access mode
$this->fileMode = FileAccessModeFactory::createReadOnlyMode();
// Get and store $file as File instance, throw an exception if failed
if(($file = File::asFile($file)) === null)
// TODO: Throw a better exception!
throw new \Exception("Invalid file!");
// Set the file
$this->setFile($file);
}
/**
* Destructor.
* Close the file handler properly.
*/
public function __destruct() {
$this->close();
}
/**
* Get the file access mode which is used by the file handler to open the file.
*
* @return FileAccessMode The used file access mode.
*/
public function getFileAccessMode() {
return $this->fileMode;
}
/**
* Set the file access mode used by the file handler to open the file.
* This will reopen the file handler if the file access mode changed and it was opened already.
*
* @param FileAccessMode|string $fileMode The file access mode instance or the file access mode as a string that
* should be used by the file handler to open the file.
*
* @return bool True on success, false on failure.
*/
public function setFileAccessMode($fileMode) {
// Convert $fileMode into a FileAccessMode instance
if(($fileMode = FileAccessMode::asFileAccessMode($fileMode)) === null)
return false;
// Set the file access mode that should be used
$this->fileMode = $fileMode;
// Check whether the file handler is opened and whether the file access mode should be changed.
// If so, reopen the file handler with the proper file access mode
if($this->handler !== null)
if($this->handler->isOpened() && !$fileMode->equals($this->handler->getFileAccessMode(), false))
return $this->handler->reopen($this->fileMode);
// File access mode changed successfully, return the result
return true;
}
/**
* Get the file which is being read from.
*
* @return File|null The file to read from, or null if no file was set.
*/
public function getFile() {
return $this->file;
}
/**
* Set the file to read from. The file must be an existing file.
*
* @param File|string $file File instance or the file as a path string to read from.
*
* @return bool True if the file was successfully set, false on failure.
*/
public function setFile($file) {
// Get $file as File instance, make sure it's not null
if(($file = File::asFile($file)) === null)
return false;
// Make sure the file has changed
if($this->file === $file)
return true;
// Make sure the file is an existing file
if(!$file->isFile())
return false;
// Set the file
$this->file = $file;
// Open a new file handler if if hasn't been instantiated yet, return the result
if($this->handler === null) {
$this->handler = new FileHandler($this->file);
return true;
}
// Set the file being handled, reopen the file handler with the new file if it is opened, return the result
return $this->handler->setFile($this->file);
}
/**
* Open the file handler to read from.
*
* @param FileAccessMode|string|null $fileMode [optional] The optional file access mode that should be used to
* access the file as a file access mode instance or file access mode string. Null may be supplied to use the
* default file access mode.
* @param resource|null $context [optional] See PHPs fopen() function for more details. Null to use the default
* context.
*
* @return bool True on success, false on failure. True will be returned if the file handler was opened already.
*/
public function open($fileMode = null, $context = null) {
// TODO: Reopen the file handler if $fileMode or $context changed!
// Make sure the file handler isn't opened already
if($this->isOpened())
return true;
// Set the file access mode if it isn't null
if($fileMode !== null)
$this->setFileAccessMode($fileMode);
// Construct a new file handler, return the result
if($this->handler === null)
$this->handler = new FileHandler($this->file);
return $this->handler->open($this->fileMode, $context);
}
/**
* Check whether the file handler is opened.
*
* @return bool True if the file handler is opened, false if not.
*/
public function isOpened() {
// Make sure a file handler is available
if($this->handler === null)
return false;
// Check whether the file handler is opened, return the result
return $this->handler->isOpened();
}
/**
* Close the file handler to read from.
*
* @return bool True on success, false on failure. True will be returned if the file handler isn't opened.
*/
public function close() {
return $this->handler->close();
}
/**
* Get the file handler instance.
*
* @return FileHandler|null File handler instance. May return null if the file handler isn't opened yet.
*/
public function getFileHandler() {
return $this->handler;
}
/**
* Read a line from the file starting at the file pointer of the file handler.
* The file handler will be opened automatically as it's needed.
* Reading ends when the maximum number of bytes is read, if specified by $length.
* Reading ends when a new line is reached (the new line character is included in the output).
* Reading ends when the end of the file is reached.
* This method is binary safe.
*
* @param int|null $length [optional] Specifies the maximum number of bytes to read,
* null to disable a maximum length.
*
* @return string|null The read data, or null if an error occurred.
*/
public function getLine($length = null) {
// Open the file handler if it isn't opened yet
if(!$this->open())
return null;
// Get and return the line
return $this->handler->getLine($length);
}
/**
* Read a line from the file starting at the file pointer of the file handler.
* The file handler will be opened automatically as it's needed.
* This method attempts to strip HTML, PHP tags and NUL bytes.
* Reading ends when the maximum number of bytes is read, if specified by $length.
* Reading ends when a new line is reached (the new line character is included in the output).
* Reading ends when the end of the file is reached.
* This method is binary safe.
*
* @param int|null $length [optional] Specifies the maximum number of bytes to read,
* null to disable a maximum length.
* @param string $allowTags [optional] A list of allowed tags, which shouldn't be stripped by this method.
* Whitespaces aren't allowed, and tags are case-insensitive.
*
* @return string|null The read data, or null if the end of the file was reached or if an error occurred.
*/
public function getLineStripped($length = null, $allowTags = '') {
// Open the file handler if it isn't opened yet
if(!$this->open())
return null;
// Get and return the stripped line
return $this->handler->getLineStripped($length, $allowTags);
}
/**
* Read the next character from the file starting at the file pointer of the file handler.
* The file handler will be opened automatically as it's needed.
* This method is binary safe.
*
* @return string|null The read character, or null if the end of the file was reached or if an error occurred.
*/
public function getChar() {
// Open the file handler if it isn't opened yet
if(!$this->open())
return null;
// Get and return the next character
return $this->handler->getChar();
}
/**
* Read the contents of the file starting at the file pointer of the file handler.
* The file handler will be opened automatically as it's needed.
* Reading ends when the maximum number of bytes is read, specified by $length.
* Reading ends when the end of the file is reached.
* Reading ends when the stream is read buffered and it does not represent a plain file,
* at most one read of up to a number of bytes equal to the chunk size (usually 8192) is made;
* depending on the previously buffered data, the size of the returned data may be larger than the chunk size.
* This method is binary safe.
*
* @param int|null $length [optional] Specifies the maximum number of bytes to read, null to ignore this parameter
* and to use the length of the file.
*
* @return string|null The read data, or null if the end of the file was reached or if an error occurred.
*/
public function read($length = null) {
// Open the file handler if it isn't opened yet
if(!$this->open())
return null;
// Use the length of the file if no length was set
if($length === null)
$length = $this->file->getSize();
// Read and return
return $this->handler->read($length);
}
/**
* Set the file position indicator of the file handler. The file handler will be opened as it's needed.
* The new position of the indicator is measured in bytes from the beginning of the file.
* This method moves the indicator to the $whence position with $offset added.
*
* @param int $offset The offset. To move to a position before the end-of-file, you need to pass a negative value in
* $offset and set $whence to SEEK_END.
* @param int $whence Positioning origin, choose from:<br>
* SEEK_SET: Set position equal to offset bytes.<br>
* SEEK_CUR: Set position to current location plus offset.<br>
* SEEK_END: Set position to end-of-file plus offset.
*
* @return bool True on success, false on failure.
*/
public function seek($offset, $whence = SEEK_SET) {
// Open the file handler if it isn't opened yet
if(!$this->open())
return false;
// Read and return
return $this->handler->seek($offset, $whence);
}
/**
* Check whether the file we're reading from is valid. The file must be an existing file.
*
* @return bool True if the file is valid and exists, false otherwise.
*/
public function isValid() {
// Make sure $file isn't null
if($this->file === null)
return false;
// Make sure $file is an instance of File, return the result
if(!$this->file instanceof File)
return false;
// Make sure the file is valid
return $this->file->isValid();
}
}
<?php
namespace carbon\core\io\filesystem\file;
use carbon\core\io\filesystem\file\accessmode\FileAccessMode;
use carbon\core\io\filesystem\file\accessmode\FileAccessModeFactory;
use carbon\core\io\filesystem\FilesystemObject;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* The FileWriter class. This class provides an interface to easily write to files, such as log files.
*
* @author Tim Visee
* @package carbon\core\io\filesystem\file
*/
class FileWriter {
/** @var File File to write to */
private $file = null;
/** @var FileHandler File handler */
private $handler = null;
/** @var FileAccessMode File access mode used to access the file. */
private $fileMode;
/** @var string The default file access mode used if no mode was specified. */
const FILE_MODE_DEFAULT = 'w';
// TODO: Variable to set the new line character!
// TODO: More and custom file access modes!
/**
* Constructor.
*
* @param FilesystemObject|string $file File instance or the path of a file as a string of the file to write to.
* The file or file path must be valid.
* @param FileAccessMode|string|null $fileMode [optional] The file access mode instance or the file access mode
* string that should be used to access the file. Null to use the default file access mode.
*
* @throws \Exception Throws an exception on error.
*/
public function __construct($file, $fileMode = self::FILE_MODE_DEFAULT) {
// Get and store $file as File instance, throw an exception if failed
if(($file = File::asFile($file)) === null)
// TODO: Throw a better exception!
throw new \Exception("Invalid file!");
// Set the file access mode
$this->setFileAccessMode($fileMode);
// Set the file
$this->setFile($file);
}
/**
* Destructor.
* Close the file handler properly.
*/
public function __destruct() {
$this->close();
}
/**
* Get the file access mode which is used by the file writer to access the file.
*
* @return FileAccessMode The file access mode.
*/
public function getFileAccessMode() {
return $this->fileMode;
}
/**
* Set the file access mode used by the file handler.
* This will reopen the file handler if the file access mode changed and it was opened already.
*
* @param string $fileMode The file access mode that should be used by the file handler.
*
* @return bool True on success, false on failure.
*/
private function setFileAccessMode($fileMode) {
// Check whether the default file access mode should be used
if($fileMode === null)
$fileMode = FileAccessMode::asFileAccessMode(self::FILE_MODE_DEFAULT);
// Convert $fileMode into a file access mode instance
if(($fileMode = FileAccessMode::asFileAccessMode($fileMode)) === null)
return false;
// Set the file access mode that should be used
$this->fileMode = $fileMode;
// Check whether the file handler is opened and whether the file access mode should be changed.
// If so, reopen the file handler with the proper file access mode
if($this->handler !== null)
if($this->handler->isOpened() && !$fileMode->equals($this->handler->getFileAccessMode(), false))
return $this->handler->reopen($this->fileMode);
// File access mode changed successfully, return the result
return true;
}
/**
* Get the file which is being written to.
*
* @return File|null The file to write to, or null if no file was set.
*/
public function getFile() {
return $this->file;
}
/**
* Set the file to write to. The new file will automatically open if the current file was opened already.
*
* @param File|string| $file File instance or the file as a path string to write to.
*
* @return bool True if the file was successfully set, false on failure.
*/
public function setFile($file) {
// Get $file as File instance, make sure it's not null
if(($file = File::asFile($file)) === null)
return false;
// Make sure the file has changed
if($this->file === $file)
return true;
// Set the file
$this->file = $file;
// Open a new file handler if if hasn't been instantiated yet, return the result
if($this->handler === null) {
$this->handler = new FileHandler($this->file);
return true;
}
// Set the file being handled, reopen the file handler with the new file if it is opened, return the result
return $this->handler->setFile($this->file);
}
/**
* Open the file handler to write to.
*
* @param FileAccessMode|string|null $fileMode [optional] The optional file access mode that should be used to
* access the file as a file access mode instance or file access mode string. Null may be supplied to use the
* default file access mode.
* @param resource|null $context [optional] See PHPs fopen() function for more details. Null to use the default
* context.
*
* @return bool True on success, false on failure. True will be returned if the file handler was opened already.
*/
public function open($fileMode = null, $context = null) {
// TODO: Reopen the file handler if $fileMode or $context changed!
// Make sure the file handler isn't opened already
if($this->isOpened())
return true;
// Set the file access mode if it isn't null
if($fileMode !== null)
$this->setFileAccessMode($fileMode);
// Construct a new file handler, return the result
if($this->handler === null)
$this->handler = new FileHandler($this->file);
return $this->handler->open($this->fileMode, $context);
}
/**
* Check whether the file handler is opened.
*
* @return bool True if the file handler is opened, false if not.
*/
public function isOpened() {
// Make sure a file handler is available
if($this->handler === null)
return false;
// Check whether the file handler is opened, return the result
return $this->handler->isOpened();
}
/**
* Close the file handler to write to.
*
* @return bool True on success, false on failure. True will be returned if the file handler isn't opened.
*/
public function close() {
return $this->handler->close();
}
/**
* Get the file handler instance.
*
* @return FileHandler|null File handler instance. May return null if the file handler isn't opened yet.
*/
public function getFileHandler() {
return $this->handler;
}
/**
* Write a string to the file. The file handler will be opened automatically if it hasn't been opened yet.
* This method is binary safe.
*
* @param string $str The string to write to the file.
*
* @return int|null Returns the number of written bytes, null on failure.
*/
public function write($str) {
// Open the file handler if it isn't opened yet
if(!$this->open())
return false;
// Write the string to the file, return the result
return $this->handler->write($str);
}
/**
* Write a line to the file. The file handler will be opened automatically if it hasn't been opened yet.
* This method is binary safe.
*
* @param string $str The string to write to the file.
*/
public function writeLine($str) {
// Write a line to the file
$this->write($str . FileHelper::getNewLineChar());
}
/**
* Set the file position indicator of the file handler. The file handler will be opened as it's needed.
* The new position of the indicator is measured in bytes from the beginning of the file.
* This method moves the indicator to the $whence position with $offset added.
*
* @param int $offset The offset.
* To move to a position before the end-of-file, you need to pass a negative value in $offset and set
* $whence to SEEK_END.
* @param int $whence Positioning origin, choose from:
* SEEK_SET: Set position equal to offset bytes.
* SEEK_CUR: Set position to current location plus offset.
* SEEK_END: Set position to end-of-file plus offset.
*
* @return bool True on success, false on failure.
*/
public function seek($offset, $whence = SEEK_SET) {
// Open the file handler if it isn't opened yet
if(!$this->open())
return false;
// Read and return
return $this->handler->seek($offset, $whence);
}
/**
* Check whether the file we're writing to is valid.
*
* @return bool True if the file is valid, false otherwise.
*/
public function isValid() {
// Make sure $file isn't null
if($this->file === null)
return false;
// Make sure $file is an instance of File, return the result
if(!$this->file instanceof File)
return false;
// Make sure the file is valid
return $this->file->isValid();
}
/**
* Check whether the file we're writing to exists.
*
* @return bool True if the target file exists, false otherwise.
*/
public function exists() {
// Make sure the file is valid
if(!$this->isValid())
return false;
// Check whether the file exists, return the result
return $this->file->exists();
}
}
<?php
/**
* FileSystemObject.php
* The FileSystemObject class, which is used to manage objects in the filesystem.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2014. All rights reserved.
*/
namespace carbon\core\io\filesystem;
use carbon\core\cache\simplecache\SimpleCache;
use carbon\core\io\filesystem\directory\Directory;
use carbon\core\io\filesystem\file\File;
use carbon\core\io\filesystem\symboliclink\SymbolicLink;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Filesystem Object class.
* This class references to an object in the filesystem based on it's file path.
* This class could be used to manage objects in the filesystem.
*
* @package carbon\core\io\filesystem
* @author Tim Visee
*/
class FilesystemObject {
/** @var string Defines the path. */
protected $path = '';
/** @var SimpleCache Instance used for basic caching. */
protected $cache;
// TODO: Should we keep these cache methods in this updated class?
/** Defines the cache key used for the normalized path cache */
const CACHE_NORMALIZED_PATH = 1;
/** Defines the cache key used for the absolute path cache */
const CACHE_ABSOLUTE_PATH = 2;
/** Defines the cache key used for the canonical path cache */
const CACHE_CANONICAL_PATH = 3;
/**
* FileSystemObject constructor.<br>
* The path of a filesystem object must be entered as argument when constructing the the Filesystem Object.
* The specified object doesn't need to exist. An optional child path relative to the $path param may be supplied.
*
* @param string|FilesystemObject $path The filesystem object as path or as filesystem object.
* @param string|null $child [optional] An optional child path relative to the main path parameter.
* Null to just use the $path param as path.
*
* @throws \Exception Throws an exception when the $path or $child param is invalid.
*/
public function __construct($path = '', $child = null) {
// Initialize the simple cache
$this->cache = new SimpleCache();
// Set the path, throw an exception on error
if(!$this->setPath($path, $child, false))
// TODO: Invalid path, throw a custom exception!
throw new \Exception();
}
/**
* Get the path of the filesystem object as a string. This will return the path in the same format without any
* processing since the path was set.
*
* @return string The filesystem object path as a string.
*/
public function getPath() {
return $this->path;
}
/**
* Set the path of the filesystem object. An optional child path relative to the $path param may be supplied.
*
* @param FilesystemObject|string $path Path to the filesystem object or the instance of another filesystem object
* to use it's path.
* @param string|null $child [optional] An optional child path relative to the $path param.
* Null to just use the $path param as path.
* @param bool $flushCache [optional] True to flush the cache, false otherwise.
*
* @return bool True on success, false on failure.
*/
protected function setPath($path, $child = null, $flushCache = true) {
// Combine the two paths and make sure it's valid
if(($path = FilesystemObjectHelper::getCombinedPath($path, $child)) === null)
return false;
// Set the path
$this->path = $path;
// Flush the cache, and return true
if($flushCache)
$this->flushCache();
return true;
}
/**
* Check whether the filesystem object exists.
*
* @return bool True if the filesystem object exists, false otherwise.
*/
public function exists() {
return FilesystemObjectHelper::exists($this);
}
/**
* Check whether the filesystem object exists and is a file.
*
* @return bool True if the filesystem object exists and is a file, false otherwise.
*/
public function isFile() {
return FilesystemObjectHelper::isFile($this);
}
/**
* Check whether the filesystem object exists and is a directory.
*
* @return bool True if the filesystem object exists and is a directory, false otherwise.
*/
public function isDirectory() {
return FilesystemObjectHelper::isDirectory($this);
}
/**
* Check whether the filesystem object exists and is a symbolic link.
*
* @return bool True if the filesystem object exists and is a symbolic link, false otherwise.
*/
public function isSymbolicLink() {
return FilesystemObjectHelper::isSymbolicLink($this);
}
/**
* Check whether the file or directory exists and is readable.
*
* @return bool True if the file or directory exists and is readable, false otherwise.
* False will also be returned if the path was invalid.
*/
public function isReadable() {
return FilesystemObjectHelper::isReadable($this);
}
/**
* Check whether the file or directory exists and is writable.
*
* @return bool True if the file or directory exists and is writable, false otherwise.
* False will also be returned if the path was invalid.
*/
public function isWritable() {
return FilesystemObjectHelper::isWritable($this);
}
/**
* Alias of {@link FileSystemObject::isWritable()}.
* Check whether the file or directory exists and is writable.
*
* @return bool True if the file or directory exists and is writable, false otherwise.
* False will also be returned if the path was invalid.
*/
public function isWriteable() {
return FilesystemObjectHelper::isWriteable($this);
}
/**
* Get the basename of the filesystem object.
* For files, this will return the file name with it's extension.
* For directories and symbolic links, this will return the name of the directory or symbolic link.
*
* @param string|null $suffix [optional] Suffix to omit from the basename. Null to ignore this feature.
*
* @return string|null Basename of the filesystem object or null on failure.
*/
public function getBasename($suffix = null) {
return FilesystemObjectHelper::getBasename($this, $suffix);
}
/**
* Get the parent directory of the filesystem object.
* This will return the directory the filesystem object is located in.
* Calling this method on the root directory will fail because the root doesn't have a parent directory,
* and will return the $default value.
*
* @return Directory|null The parent directory as Directory instance, or null if there's no parent directory and on
* failure.
*/
public function getParent() {
return FilesystemObjectHelper::getParent($this);
}
/**
* Check whether the filesystem object has a parent directory.
*
* @return bool True if the filesystem object has a parent directory, false otherwise.
*/
public function hasParent() {
return FilesystemObjectHelper::hasParent($this);
}
/**
* Get the normalized path of the filesystem object.
* This will remove unicode whitespaces and any kind of self referring or parent referring paths.
* The filesystem object doesn't need to exist.
*
* @return string|null A normalized path of the filesystem object, or null on failure.
*/
public function getNormalizedPath() {
// Return the normalized path if it's cached
if($this->cache->has(self::CACHE_NORMALIZED_PATH))
return $this->cache->get(self::CACHE_NORMALIZED_PATH, $this->path);
// Get the normalized path
$path = FilesystemObjectHelper::getNormalizedPath($this);
// Cache and return the normalized path
$this->cache->set(self::CACHE_NORMALIZED_PATH, $path);
return $path;
}
/**
* Get the absolute path of the filesystem object.
* A canonicalized version of the absolute path will be returned if the filesystem object exists.
*
* @return string|null Absolute path of the filesystem object or null on failure.
*/
public function getAbsolutePath() {
// Return the absolute path if it's cached
if($this->cache->has(self::CACHE_ABSOLUTE_PATH))
return $this->cache->get(self::CACHE_ABSOLUTE_PATH, $this->path);
// Get the absolute path
$path = FilesystemObjectHelper::getAbsolutePath($this);
// Cache and return the absolute path
$this->cache->set(self::CACHE_ABSOLUTE_PATH, $path);
return $path;
}
/**
* Get the canonicalized path of the filesystem object. The canonicalized path will be absolute.
* A path which is invalid or doesn't exist will be canonicalized as far as that's possible.
*
* @return string|null Canonicalized path of the filesystem object, or null if failed to canonicalize the path.
*/
// TODO: Improve this method!
public function getCanonicalPath() {
// Return the canonical path if it's cached
if($this->cache->has(self::CACHE_CANONICAL_PATH))
return $this->cache->get(self::CACHE_CANONICAL_PATH, null);
// Get the canonicalized path
$path = FilesystemObjectHelper::getCanonicalPath($this);
// Cache and return the canonical path
$this->cache->set(self::CACHE_CANONICAL_PATH, $path);
return $path;
}
/**
* Canonicalize the path.
*/
public function canonicalize() {
$this->setPath($this->getCanonicalPath());
}
/**
* Delete the filesystem object if it exists.
* Directories will only be deleted if they're empty or if the $recursive param is set to true.
*
* @param resource $context [optional] See the unlink() function for documentation.
* @param bool $recursive [optional] True to delete directories recursively.
* This option should be true if directories with contents should be deleted.
*
* @return int Number of deleted filesystem objects, a negative number will be returned if the $path param was
* invalid.
*
* @see unlink()
*/
public function delete($context = null, $recursive = false) {
return FilesystemObjectHelper::delete($this, $recursive, $context);
}
/**
* Rename a file or directory. The filesystem object must exist.
*
* @param FileSystemObject|string $newPath The filesystem object instance of the path of the filesystem object to
* rename the object to. This filesystem object or path should include the full path. The object may only exist if
* $overwrite is set to true or the renaming will fail.
* @param bool $overwrite [optional] True to overwrite the existing filesystem object when the target name already
* exist, false otherwise.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return bool True if the filesystem object was successfully renamed, false on failure.
*
* @see rename();
*/
public function rename($newPath, $overwrite = false, $context = null) {
return FilesystemObjectHelper::rename($this, $newPath, $overwrite, $context);
}
/**
* Move a file system object. The filesystem object that should be moved must exist.
*
* @param FileSystemObject|string $target The filesystem object instance or the path of a the filesystem object to
* move the object to. This filesystem object or path should be a full/absolute path. If the target is an existing
* directory, the path won't be moved to the target, instead the path is moved inside the target directory. This is
* still the case if $overwrite is set to true.
* @param bool $overwrite True to overwrite the target with the path if the target already exists. Please note that
* the path is moved into the target directory if the target is an existing directory, thus the target directory
* won't be overwritten. If the path being moved does exist inside the target directory, the object will be
* overwritten if $overwrite is set to true.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return bool True on success, false on failure.
*
* @see rename();
*/
public function move($target, $overwrite = false, $context = null) {
return FilesystemObjectHelper::move($target, $overwrite, $context);
}
/**
* Flush the cache
*/
public function flushCache() {
$this->cache->flush();
}
/**
* Get a File, Directory, SymbolicLink or FileSystemObject instance.
* A File instance will be returned if the filesystem object is a file.
* A Directory instance will be returned if the filesystem object is a directory.
* A SymbolicLink instance will be returned if the filesystem object is a symbolic link.
* A FileSystemObject instance will be returned if it couldn't be determined whether the filesystem object is a
* file, directory or symbolic link. This is usually the case when the filesystem object doesn't exist.
* The supplied filesystem object doesn't need to exist.
* An optional child path relative to the $path param may be supplied.
*
* @param FilesystemObject|string $path Path to the filesystem object or the instance of another filesystem object
* to use it's path.
* @param string|null $child [optional] An optional child path relative to the $path param.
* Null to just use the $path param as path.
*
* @return File|Directory|SymbolicLink|FilesystemObject|null File, Directory, SymbolicLink or FileSystemObject
* instance, or null if the $path was invalid.
*/
// TODO: Should we rename this method to instance(), instantiate() or parse(). At least use better naming than from().
// TODO: Should we keep this method available, even though the constructor is available with similar functionality
public static function from($path, $child = null) {
// Create a filesystem object instance and make sure it's valid
if(($path = FilesystemObjectHelper::getCombinedPath($path, $child)) === null)
return null;
// Return a File instance if the filesystem object is a file
if(FilesystemObjectHelper::isFile($path))
return new File($path);
// Return a Directory instance if the filesystem object is a directory
if(FilesystemObjectHelper::isDirectory($path))
return new Directory($path);
// Return a SymbolicLink instance if the filesystem object is a symbolic link
if(FilesystemObjectHelper::isSymbolicLink($path))
return new SymbolicLink($path);
// Return as filesystem object instance
return new FilesystemObject($path);
}
/**
* Check whether the filesystem object path is valid. The filesystem object doesn't need to exist.
*
* @return bool True if the path of the filesystem object seems to be valid, false otherwise.
*/
// TODO: Create static function of this, and check the path on construction.
public function isValid() {
return FilesystemObjectHelper::isValid($this);
}
/**
* Compare this filesystem object with an other filesystem object.
*
* @param FilesystemObject|string $other The other filesystem object instance.
* The path of a filesystem object may be supplied if $sameType is set to false to just compare the paths.
* @param bool $sameType [optional] True to make sure both instances are from the same type,
* false to just compare the paths.
*
* @return bool True if this filesystem object is equal with $other, false otherwise.
* False will also be returned on failure.
*/
// TODO: Improve the quality of this method!
public function equals($other, $sameType = true) {
// Make sure the types are equal
if($sameType && !(get_class() === get_class($other)))
return false;
// Convert $other into a string, return false if failed
if(($other = FilesystemObjectHelper::asPath($other, false)) === null)
return false;
// Compare the paths, return the result
return StringUtils::equals($this->getPath(), $other, false, true);
}
/**
* Convert the path to a string. The output of {@link getPath()} will be returned.
*
* @return string Path as a string.
*/
public function __toString() {
return $this->path;
}
/**
* Clone this instance.
*
* @return FilesystemObject Cloned instance
*/
public function __clone() {
// Get the class type
$class = get_class($this);
// Clone and return the instance
return new $class($this->path);
}
// TODO: Take a look at the getCanonicalPath, getAbsolutePath and canonicalize methods!
// TODO: Method to convert anything possible into a path
}
<?php
/**
* FileSystemObjectArrayUtils.php
* The FileSystemObjectArrayUtils class, which is used to manage arrays of objects in the filesystem.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2014. All rights reserved.
*/
// TODO: Implement caching
// TODO: Should we rename this to FilesystemObjectSetUtils or something similar?
namespace carbon\core\io\filesystem;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Filesystem Object Array Utils class.
* This utilities class could be used to manage arrays of objects in the filesystem.
*
* @package carbon\core\io\filesystem
* @author Tim Visee
*/
class FilesystemObjectArrayHelper {
/**
* Get the sources of a list of filesystem object as an array of strings.
*
* @param Array|FilesystemObject|string $paths An array with filesystem object instances or path strings.
* Or a single filesystem object instance or path string. The array may contain multiple other arrays.
* @param bool $ignoreInvalid True to ignore invalid items in the array, this will prevent the default value from
* being returned.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return Array|mixed|null An array of path strings, or the $default value on failure.
*/
public static function asPathArray($paths, $ignoreInvalid = false, $default = null) {
// Create an array to push all the parents in
$out = Array();
// Make sure $parents isn't null
if ($paths === null)
return null;
// Create an array of the $parents param
if (!is_array($paths))
$paths = Array($paths);
// Process each filesystem object
$pathCount = sizeof($paths);
for ($i = 0; $i < $pathCount; $i++) {
$path = $paths[$i];
// Check whether $path is an array
if (is_array($path)) {
// Get all parents from the array and make sure the result is valid
if (($arrayPaths = self::asPathArray($path, $ignoreInvalid)) === null)
return $default;
// Push the parents into the array
$out = array_merge($out, $arrayPaths);
continue;
}
// Get the path as a string
$path = FilesystemObjectHelper::asPath($path, false);
// Check whether the path is valid
if (is_string($path)) {
array_push($out, $path);
continue;
}
// Check whether we should return the default value because the conversion of this path failed
if (!$ignoreInvalid)
return $default;
}
// Return the array of parents
return $out;
}
/**
* Combine parent and child sources into a sources.
*
* If only one parent path is set a path will be created for each child with the same parent path.
* If only one child path is set a path will be created for each parent with the same child path.
* A single path will be returned if only a single path is supplied as parent and child.
* If no child sources is set, the list of parents will be returned interpreted as regular sources.
*
* @param FileSystemObject|string|array $parents An array with filesystem object instances or path strings to use as
* parents. Or a single filesystem object instance or path string. The array may contain multiple other arrays.
* @param FileSystemObject|string|array|null $children [optional] An array with filesystem object instances or path strings to
* use as child's. Or a single filesystem object instance or path string. The array may contain multiple other arrays.
* If this parameter equals null the list of parents will be returned as array.
* @param bool $includeInvalid [optional] True to include null items in the array being returned for parent and child
* combinations that couldn't be combined.
* @param null $default [optional] The default value to return on failure.
*
* @return array An array of sources.
*/
public static function getCombinedPaths($parents, $children = null, $includeInvalid = true, $default = null) {
// Convert $sources and $children into an array of sources, and make sure $sources is valid
if(($parents = self::asPathArray($parents, false, null)) === null)
return $default;
$children = self::asPathArray($parents, false, null);
// Count the number of parents and set whether only a single parent is used
$parentCount = sizeof($parents);
$singlePath = ($parentCount == 1);
// Make sure any child is available, if not return the list of sources
if($children === null)
return $parents;
if(($childCount = sizeof($children)) <= 0)
return $parents;
// Check whether only a single child is used
$singleChild = ($childCount == 1);
// Create a new array to put all sources in
$paths = Array();
// Loop through all the parents
for($parentIndex = 0; $parentIndex < $parentCount; $parentIndex++) {
// Choose what child's to loop through
$childFirst = 0;
$childLast = 0;
if($singlePath)
$childLast = $childCount - 1;
elseif(!$singleChild)
$childFirst = ($childLast = $parentIndex);
// Loop through all the child's
for($childIndex = $childFirst; $childIndex <= $childLast; $childIndex++) {
// Get the base parent and child
$parent = $parents[$parentIndex];
$child = $children[$childIndex];
// Combine the base path and make sure it's valid if invalid items may not be implemented
if(($path = FilesystemObjectHelper::getCombinedPath($parent, $child, null)) === null && !$includeInvalid)
continue;
// Add the path to the sources list
array_push($paths, $path);
}
}
// Return the path list
return $paths;
}
/**
* Check whether the filesystem objects exist.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or as single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources from the arra exist, false to just check whether at least
* one exists.
*
* @return bool True if the filesystem objects exist. If $all is set to true, all filesystem objects exist when true
* is returned, if $all is set to false at least one of the filesystem objects exists.
*/
public static function exists($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to check whether they exist
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path exists if only one path has to be valid
if(($exists = FilesystemObjectHelper::exists($paths[$i])) && !$all)
return true;
// Make sure the path exists if all sources must exist
if(!$exists && $all)
return false;
}
// Return the result. True if all sources needed to exist, false if just one had to exist
return $all;
}
/**
* Check how many file system objects exist.
*
* @param array|FileSystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or as a single filesystem object instance or filesystem object path as a string.
*
* @return int The number of filesystem objects that exist. Null will be returned if the $sources parameter is invalid.
*/
public static function existCount($paths) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Keep track of the number of existing sources
$existing = 0;
// Count the number of sources and loop through each path to check whether it exists
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Check whether the path exists if only one path has to be valid
if(FilesystemObjectHelper::exists($paths[$i]))
$existing++;
// Return the number of existing sources
return $existing;
}
/**
* Check whether an array of filesystem objects exists and are files.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are files, false to just check whether
* at least one is a file.
*
* @return bool True if the filesystem objects are files. If $all is set to true, all filesystem object are files
* when true is returned, if $all is set to false at least one of the filesystem objects is a file.
*/
public static function areFiles($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to validate it
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is a file if only one path has to be a file
if(($isFile = FilesystemObjectHelper::isFile($paths[$i])) && !$all)
return true;
// Make sure the path is file if all sources must be files
if(!$isFile && $all)
return false;
}
// Return the result. True if all sources needed to be an existing file, false if just one had to be a file
return $all;
}
/**
* Check whether an array of filesystem objects exists and are directories.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are directories, false to just check
* whether at least one is a directory.
*
* @return bool True if the filesystem objects are directories. If $all is set to true, all filesystem object are
* directories when true is returned, if $all is set to false at least one of the filesystem objects is a directory.
*/
public static function areDirectories($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to validate it
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is a directory if only one path has to be a directory
if(($isDir = FilesystemObjectHelper::isDirectory($paths[$i])) && !$all)
return true;
// Make sure the path is a directory if all sources must be directories
if(!$isDir && $all)
return false;
}
// Return the result. True if all sources needed to be an existing directory, false if just one had to be a directory
return $all;
}
/**
* Check whether an array of filesystem objects exists and are symbolic links.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are symbolic links, false to just check
* whether at least one is a symbolic link.
*
* @return bool True if the filesystem objects are symbolic links. If $all is set to true, all filesystem object are
* symbolic links when true is returned, if $all is set to false at least one of the filesystem objects is a
* symbolic link.
*/
public static function areSymbolicLinks($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to validate it
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is a symbolic link if only one path has to be a symbolic link
if(($isSymLink = FilesystemObjectHelper::isDirectory($paths[$i])) && !$all)
return true;
// Make sure the path is a symbolic link if all sources must be symbolic links
if(!$isSymLink && $all)
return false;
}
// Return the result. True if all sources needed to be an existing symbolic link, false if just one had to be a symbolic link
return $all;
}
/**
* Check whether an array of filesystem objects are readable.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are readable, false to just check whether
* at least one is a readable.
*
* @return bool True if the filesystem objects are readable. If $all is set to true, all filesystem object are
* readable when true is returned, if $all is set to false at least one of the filesystem objects is readable.
*/
public static function areReadable($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to check whether it's readable
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is a readable if only one path has to be readable
if(($isReadable = FilesystemObjectHelper::isReadable($paths[$i])) && !$all)
return true;
// Make sure the path is a readable if all sources must be readable
if(!$isReadable && $all)
return false;
}
// Return the result. True if all sources needed to be readable, false if just one had to be a readable
return $all;
}
/**
* Check whether an array of filesystem objects are writable.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are writable, false to just check whether
* at least one is a writable.
*
* @return bool True if the filesystem objects are writable. If $all is set to true, all filesystem object are
* writable when true is returned, if $all is set to false at least one of the filesystem objects is writable.
*/
public static function areWritable($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to check whether it's writable
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is a writable if only one path has to be writable
if(($isWritable = FilesystemObjectHelper::isWritable($paths[$i])) && !$all)
return true;
// Make sure the path is a writable if all sources must be writable
if(!$isWritable && $all)
return false;
}
// Return the result. True if all sources needed to be writable, false if just one had to be a writable
return $all;
}
/**
* Check whether an array of filesystem objects are writeable.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are writeable, false to just check whether
* at least one is a writeable.
*
* @return bool True if the filesystem objects are writeable. If $all is set to true, all filesystem object are
* writeable when true is returned, if $all is set to false at least one of the filesystem objects is writeable.
*/
public static function areWriteable($paths, $all = true) {
return self::areWritable($paths, $all);
}
/**
* Get the basename for each filesystem object.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param string|null $suffix [optional] Suffix to omit from each basename. Null to ignore this feature.
* @param mixed|null $default [optional] A default value that will be put into the array if the basename of a
* specific filesystem object couldn't be found.
*
* @return array An array of basenames.
*/
public static function getBasenames($paths, $suffix = null, $default = null) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return null;
// Create an array to put all the basenames in
$names = Array();
// Count the number of sources and loop through each path to get it's basename
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Get the basename of the path and add it to the basenames array
array_push($names, FilesystemObjectHelper::getBasename($paths[$i], $suffix, $default));
// Return the list of basenames
return $names;
}
/**
* Get the parent for each filesystem object.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object sources as a string.
* @param mixed|null $default [optional] A default value that will be put into the array if the parent of a specific
* filesystem object couldn't be found.
*
* @return array An array of parents.
*/
public static function getParents($paths, $default = null) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return null;
// Create an array to put all the parents in
$parents = Array();
// Count the number of sources and loop through each path to get it's parent
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Get the parent of the path and add it to the parents array
array_push($parents, FilesystemObjectHelper::getParent($paths[$i], $default));
// Return the list of parents
return $parents;
}
/**
* Check whether an array of filesystem objects has parents.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array have parents, false to just make sure at
* least one of the sources has a parent.
*
* @return bool True if the filesystem objects have a parent. If $all is set to true, all filesystem object have a
* parent when true is returned, if $all is set to false at least one of the filesystem objects has a parent.
*/
public static function hasParents($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to check whether each one has a parent or not
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path has a parent or not
if(($hasParent = FilesystemObjectHelper::hasParent($paths[$i])) && !$all)
return true;
// Make sure the path has a parent if all must have a parent
if(!$hasParent && $all)
return false;
}
// Return the result. True if all sources needed to have a parent, false if just one has a parent
return $all;
}
/**
* Get the normalized path for each filesystem object.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param mixed|null $default [optional] A default value that will be put into the array if the normalized path of a
* specific filesystem object couldn't be found.
*
* @return array An array of normalized sources.
*/
public static function getNormalizedPaths($paths, $default = null) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return null;
// Create an array to put all the normalized sources in
$normalized = Array();
// Count the number of sources and loop through each path to get it's normalized path
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Get the normalized path and put it into the array
array_push($normalized, FilesystemObjectHelper::getNormalizedPath($paths[$i], $default));
// Return the list of normalized sources
return $normalized;
}
/**
* Get the absolute path for each filesystem object.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param mixed|null $default [optional] A default value that will be put into the array if the absolute path of a
* specific filesystem object couldn't be found.
*
* @return array An array of absolute sources.
*/
public static function getAbsolutePaths($paths, $default = null) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return null;
// Create an array to put all the absolute sources in
$absolute = Array();
// Count the number of sources and loop through each path to get it's absolute path
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Get the absolute path and put it into the array
array_push($absolute, FilesystemObjectHelper::getAbsolutePath($paths[$i], $default));
// Return the list of absolute sources
return $absolute;
}
/**
* Get the canonical path for each filesystem object.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param mixed|null $default [optional] A default value that will be put into the array if the canonical path of a
* specific filesystem object couldn't be found.
*
* @return array An array of canonical sources.
*/
public static function getCanonicalPaths($paths, $default = null) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return null;
// Create an array to put all the canonical sources in
$canonical = Array();
// Count the number of sources and loop through each path to get it's canonical path
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++)
// Get the canonical path and put it into the array
array_push($canonical, FilesystemObjectHelper::getCanonicalPath($paths[$i], $default));
// Return the list of canonical sources
return $canonical;
}
/**
* Delete a list of filesystem objects if they exists.
* Directories will only be deleted if they're empty unless the $recursive param is set to true.
*
* @param Array|FilesystemObject|string $paths Filesystem object instance or a path string to delete.
* Or an array with filesystem object instances or path strings. An array may contain multiple other arrays.
* @param bool $recursive [optional] True to delete directories recursively.
* This option should be true if directories with contents should be deleted.
* @param resource $context [optional] See the unlink() function for documentation.
*
* @return int Number of deleted filesystem objects, a negative number will be returned on failure. This will also
* count the number of deleted recursive filesystem objects.
*
* @see unlink()
*/
public static function delete($paths, $recursive = false, $context = null) {
// Convert the sources into a string array, return the $default value if failed
if(($paths = self::asPathArray($paths)) === null)
return -1;
// Count the deleted filesystem objects
$count = 0;
// Delete each filesystem object
$size = sizeof($paths);
for($i = 0; $i < $size; $i++) {
$path = $paths[$i];
// Delete the current item
$count += FilesystemObjectHelper::delete($path, $recursive, $context);
}
// Return the number of delete filesystem objects
return $count;
}
/**
* Rename an array of filesystem objects.
*
* @param Array|FileSystemObject|string $oldPaths The filesystem object instance or the path of the filesystem
* object as a string, or an array of filesystem objects. The filesystem object must be a existing filesystem object.
* The number of old files must be equal to the number of new files.
* @param Array|FileSystemObject|string $newPaths The filesystem object instance of the path of the filesystem
* object to rename the object to, or an array of filesystem objects. This filesystem object or path should include
* the full path. The object may only exist if $overwrite is set to true or the renaming will fail. The number of
* new files must be equal to the number of old files.
* @param bool $overwrite [optional] True to overwrite the existing filesystem object when the target name already
* exist, false otherwise.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return int The number of filesystem objects that were renamed successfully. A negative number will be returned
* if an error occurred. Zero will be returned if no filesystem object was moved.
*
* @see rename();
*/
public static function rename($oldPaths, $newPaths, $overwrite = false, $context = null) {
// Convert $sources and $destinations into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($oldPaths, false, null)) === null)
return null;
if(($paths = self::asPathArray($newPaths, false, null)) === null)
return null;
// Count the number of old and new sources
$oldCount = sizeof($oldPaths);
$newCount = sizeof($newPaths);
// Make sure there's at least one path available, and make sure count of old and new sources is equal
if($oldCount <= 0)
return 0;
if($oldCount !== $newCount)
return -1;
// Validate each old path
if(!self::exists($oldPaths, true) || self::areSymbolicLinks($oldPaths, false))
return -1;
// Make sure all new sources are valid
if(!self::areValid($newPaths, true))
return -1;
// Do an overwrite check
if(self::exists($newPaths, false) && !$overwrite)
return -1;
// Rename each path, and count the number of renamed sources
$count = 0;
for($i = 0; $i < $oldCount; $i++)
$count += (FilesystemObjectHelper::rename($oldPaths[$i], $newPaths[$i], $overwrite, $context) ? 1 : 0);
// Return the number of renamed sources
return $count;
}
/**
* Move an array of filesystem objects. The filesystem objects that should be moved must exist.
*
* @param FileSystemObject|string $sources The filesystem object must exist. The filesystem object instance or the
* path of the filesystem object as a string, or an array of filesystem objects. The filesystem object must be a
* existing filesystem object. The number of sources must be equal or greater than the number of destinations.
* @param FileSystemObject|string $destinations The filesystem object. The filesystem object instance or the
* path of the filesystem object as a string, or an array of filesystem objects. This filesystem object or path
* should be a full/absolute path. The number of destinations should equal the number of sources, or should equal one.
* If the number of destinations equals one while the number of sources is greater, all sources will be moved into the
* available target (the target must be a directory, in that case).
* @param bool $overwrite [optional] True to overwrite the target with the path if the target already exists. If only a single
* target is set while multiple sources are available, the $overwrite argument will apply to the objects being moved
* into the target directory.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return int The number of moved files, a negative number on failure.
*
* @see rename();
*/
public static function move($sources, $destinations, $overwrite = false, $context = null) {
// Convert the old and new sources into an array of sources
if(($sources = self::asPathArray($sources, false, null)) === null)
return false;
if(($destinations = self::asPathArray($destinations, false, null)) === null)
return false;
// Count the number of old and new sources
$oldCount = sizeof($sources);
$newCount = sizeof($destinations);
// Make sure there's at least one path available, and make sure the number of new sources equals the number of old sources, or equals one
if($oldCount <= 0)
return 0;
if($oldCount !== $newCount && $newCount != 1)
return -1;
// Validate each old path
if(!self::exists($sources, true) || self::areSymbolicLinks($sources, false))
return -1;
// Make sure all new sources are valid
if(!self::areValid($destinations, true))
return -1;
// Do an overwrite check
if(self::exists($destinations, false) && !$overwrite)
return -1;
// Count the number of moved filesystem objects
$count = 0;
// Check whether the old sources should all be copied into the new path (directory)
if($oldCount > 1 && $newCount == 1) {
// Get the new path
$newPath = $destinations[0];
// Make sure the new path is a directory
if(!FilesystemObjectHelper::isDirectory($newPath))
return -1;
// Loop through each path that should be copied
for($i = 0; $i < $oldCount; $i++) {
// Get the current entry
$entry = $sources[$i];
// Get the target path for this entry
$entryTarget = new FilesystemObject($newPath, FilesystemObjectHelper::getBasename($entry));
// Copy the old path
$count += FilesystemObjectHelper::move($entry, $entryTarget, $overwrite, $context) ? 1 : 0;
}
} else {
// Copy each path, and count the number of renamed sources
for($i = 0; $i < $oldCount; $i++)
$count += FilesystemObjectHelper::move($sources[$i], $destinations[$i], $overwrite, $context) ? 1 : 0;
}
// Return the number of copied sources
return $count;
}
/**
* Copy an array of filesystem objects. The filesystem objects that should be copied must exist.
*
* @param FileSystemObject|string $sources The filesystem object must exist. The filesystem object instance or the
* path of the filesystem object as a string, or an array of filesystem objects. The filesystem object must be a
* existing filesystem object. The number of sources must be equal or greater than the number of destinations.
* @param FileSystemObject|string $destinations The filesystem object. The filesystem object instance or the
* path of the filesystem object as a string, or an array of filesystem objects. This filesystem object or path
* should be a full/absolute path. The number of destinations should equal the number of sources, or should equal one.
* If the number of destinations equals one while the number of sources is greater, all sources will be copied into the
* available target (the target must be a directory, in that case).
* @param bool $overwrite [optional] True to overwrite the target with the path if the target already exists. If only a single
* target is set while multiple sources are available, the $overwrite argument will apply to the objects being copied
* into the target directory.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return int The number of copied files, a negative number on failure.
*
* @see rename();
*/
public static function copy($sources, $destinations, $overwrite = false, $context = null) {
// Convert the old and new sources into an array of sources
if(($sources = self::asPathArray($sources, false, null)) === null)
return false;
if(($destinations = self::asPathArray($destinations, false, null)) === null)
return false;
// Count the number of old and new sources
$oldCount = sizeof($sources);
$newCount = sizeof($destinations);
// Make sure there's at least one path available, and make sure the number of new sources equals the number of old sources, or equals one
if($oldCount <= 0)
return 0;
if($oldCount !== $newCount && $newCount != 1)
return -1;
// Validate each old path
if(!self::exists($sources, true) || self::areSymbolicLinks($sources, false))
return -1;
// Make sure all new sources are valid
if(!self::areValid($destinations, true))
return -1;
// Do an overwrite check
if(self::exists($destinations, false) && !$overwrite)
return -1;
// Count the number of moved filesystem objects
$count = 0;
// Check whether the old sources should all be moved into the new path (directory)
if($oldCount > 1 && $newCount == 1) {
// Get the new path
$newPath = $destinations[0];
// Make sure the new path is a directory
if(!FilesystemObjectHelper::isDirectory($newPath))
return -1;
// Loop through each path that should be moved
for($i = 0; $i < $oldCount; $i++) {
// Get the current entry
$entry = $sources[$i];
// Get the target path for this entry
$entryTarget = new FilesystemObject($newPath, FilesystemObjectHelper::getBasename($entry));
// Move the old path
$count += FilesystemObjectHelper::copy($entry, $entryTarget, $overwrite, $context) ? 1 : 0;
}
} else {
// Copy each object, and count the number of renamed sources
for($i = 0; $i < $oldCount; $i++)
$count += FilesystemObjectHelper::copy($sources[$i], $destinations[$i], $overwrite, $context) ? 1 : 0;
}
// Return the number of copied sources
return $count;
}
/**
* Validate an array of sources of filesystem objects. The filesystem objects don't need to exist to be valid.
*
* @param array|FilesystemObject|string $paths An array containing filesystem objects or filesystem object sources as
* a string. Or a single filesystem object instance or filesystem object path as a string.
* @param bool $all [optional] True to make sure all sources form the array are valid, false to just check whether
* at least one is valid.
*
* @return bool True if the filesystem objects are valid. If $all is set to true, all filesystem object are valid
* when true is returned, if $all is set to false at least one of the filesystem objects is valid.
*/
public static function areValid($paths, $all = true) {
// Convert $sources into an array of sources, and make sure it's valid
if(($paths = self::asPathArray($paths, false, null)) === null)
return false;
// Count the number of sources and loop through each path to validate it
$pathCount = sizeof($paths);
for($i = 0; $i < $pathCount; $i++) {
// Check whether the path is valid if only one path has to be valid
if(($valid = FilesystemObjectHelper::isValid($paths[$i])) && !$all)
return true;
// Make sure the path is valid if all sources must be valid
if(!$valid && $all)
return false;
}
// Return the result. True if all sources needed to be valid, false if just one had to be valid
return $all;
}
// TODO: All methods that return an integer should return -1 OR null on error?
}
<?php
namespace carbon\core\io\filesystem;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
interface FilesystemObjectFlags {
/**
* Unknown file type flag. This flag is used for filesystem objects that aren't recognized as a file, directory or
* symbolic link, maybe because it might not exist.
*/
const FLAG_TYPE_OBJECT = 1;
/** File type flag. This flag is used for filesystem objects that are files. */
const FLAG_TYPE_FILE = 2;
/** Directory type flag. This flag is used for filesystem objects that are directories. */
const FLAG_TYPE_DIRECTORY = 4;
/** Symbolic link type flag. This flag is used for filesystem objects that are symbolic links. */
const FLAG_TYPE_SYMBOLIC_LINK = 8;
/**
* All object types flag. This flag is used for:
* - FLAG_TYPE_OBJECT
* - FLAG_TYPE_FILE
* - FLAG_TYPE_DIRECTORY
* - FLAG_TYPE_SYMBOLIC_LINK
*/
const FLAG_TYPE_ALL = 15; // 1 + 2 + 4 + 8
// TODO: Improve quality of this interface!
}
<?php
/**
* FileSystemObjectUtils.php
* The FileSystemObjectUtils class, which is used to manage objects in the filesystem.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2014. All rights reserved.
*/
// TODO: Implement caching
namespace carbon\core\io\filesystem;
use carbon\core\io\filesystem\directory\Directory;
use carbon\core\io\filesystem\symboliclink\SymbolicLink;
use carbon\core\io\filesystem\symboliclink\SymbolicLinkHelper;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Filesystem Object Utils class.
* This utilities class could be used to manage objects in the filesystem.
*
* @package carbon\core\io\filesystem
* @author Tim Visee
*/
class FilesystemObjectHelper {
// TODO: Move all logic to the master class if possible! (Do this for the sub-classes too)
// TODO: Rename this to a Utils class! (Do this for the sub-classes too)
// TODO: Add method to get the file URI!
// TODO: Add a method to get the filesystem object from an URI!
/**
* Get the oldPath of a filesystem object as a string. The oldPath isn't validated unless $validate is set to true.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
* @param bool $validate [optional] True if the oldPath should be validated, false to skip any validation.
* @param mixed|null $default [optional] Default value returned if the oldPath couldn't be determined,
* possibly because the $oldPath param was invalid.
*
* @return string|mixed The oldPath of the filesystem object as a string. Or the $default param value on failure.
* The $default value will also be returned if the oldPath was invalid while $validate was set to true.
*/
// TODO: Should we rename this method?
public static function asPath($path, $validate = false, $default = null) {
// Return the oldPath if it's a string already
if(is_string($path))
return $path;
// Return the oldPath if it's a filesystem object instance
if($path instanceof FilesystemObject) {
// Validate the oldPath
if($validate)
if(!$path->isValid())
return $default;
// Return the oldPath
return $path->getPath();
}
// Return the default value
return $default;
}
/**
* Combine a parent and child oldPath, where the parent oldPath is the base oldPath, and the child oldPath is relative to the
* base oldPath.
*
* @param FilesystemObject|string $parent Parent oldPath or filesystem object instance to use as base.
* @param string $child [optional] The child oldPath relative to the parent oldPath. Null to just use the parent oldPath.
* @param mixed|null $default [optional] An optional default value, that will be returned if the $oldPath or $child
* param was invalid.
*
* @return string|mixed|null
*/
public static function getCombinedPath($parent, $child = null, $default = null) {
// Convert the oldPath into a absolute oldPath string, return the $default value if failed
if(($parent = self::getAbsolutePath(self::asPath($parent, false))) === null)
return $default;
// Check whether we should suffix the child oldPath, if not, return the oldPath
if(empty($child))
return $parent;
// Make sure the $child param is a string
if(!is_string($child))
return $default;
// Trim directory separators from both oldPath
// TODO: Is the coded below unnecessary?
$parent = rtrim($parent, '/\\');
$child = ltrim($child, '/\\');
// Combine and return the base oldPath with the child oldPath
return $parent . DIRECTORY_SEPARATOR . $child;
}
/**
* Check whether a filesystem object exists.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the filesystem object exists, false otherwise.
*/
public static function exists($path) {
// Convert the oldPath into a string, return false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check if the object exists, return the result
return file_exists($path);
}
/**
* Check whether a filesystem object exists and is a file.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the filesystem object exists and is a file, false otherwise.
*/
public static function isFile($path) {
// Convert the oldPath into a string, return false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check if the object exists and is a file, return the result
return is_file($path);
}
/**
* Check whether a filesystem object exists and is a directory.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the filesystem object exists and is a directory, false otherwise.
*/
public static function isDirectory($path) {
// Convert the oldPath into a string, return false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check if the object exists and is a directory, return the result
return is_dir($path);
}
/**
* Check whether a filesystem object exists and is a symbolic link.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the filesystem object exists and is a symbolic link, false otherwise.
*/
public static function isSymbolicLink($path) {
// Convert the oldPath into a string, return false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check if the object exists and is a symbolic link, return the result
return is_link($path);
}
/**
* Check whether a file or directory exists and is readable.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the file or directory exists and is readable, false otherwise.
* False will also be returned if the oldPath was invalid.
*/
public static function isReadable($path) {
// Convert the file into a oldPath string, return the $default value if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check whether the file is readable, return the result
return is_readable($path);
}
/**
* Check whether a file or directory exists and is writable.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the file or directory exists and is writable, false otherwise.
* False will also be returned if the oldPath was invalid.
*/
public static function isWritable($path) {
// Convert the file into a oldPath string, return the $default value if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Check whether the file is writable, return the result
return is_writable($path);
}
/**
* Alias of {@link FilesystemObjectHelper::isWritable()}.
* Check whether a file or directory exists and is writable.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the file or directory exists and is writable, false otherwise.
* False will also be returned if the oldPath was invalid.
*/
public static function isWriteable($path) {
return self::isWritable($path);
}
/**
* Get the basename of a filesystem object.
* For files, this will return the file name with it's extension.
* For directories and symbolic links, this will return the name of the directory or symbolic link.
* The $default value will be returned on failure.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
* @param string|null $suffix [optional] Suffix to omit from the basename. Null to ignore this feature.
* @param mixed|null $default [optional] A default value that will be returned on failure.
*
* @return string|mixed|null Basename of the filesystem object, or the $default value on failure.
*/
public static function getBasename($path, $suffix = null, $default = null) {
// Convert the oldPath into a string, return the default value if failed
if(($path = self::asPath($path, false)) === null)
return $default;
// Get and return the basename
return basename($path, $suffix);
}
/**
* Get the parent directory of a filesystem object.
* This will return the directory the filesystem object is located in.
* Running this method against the root directory will fail because it doesn't have a prent directory,
* and will return the $default value.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
* @param mixed|null $default [optional] A default value that will be returned if the filesystem object doesn't
* have a parent directory and on failure.
*
* @return Directory|mixed|null The parent directory as Directory instance, or the $default value if the object
* doesn't have a parent directory or on failure.
*/
public static function getParent($path, $default = null) {
// Convert the oldPath into a string, return the default value if failed
if(($path = self::asPath($path, false)) === null)
return $default;
// Get the parent directory oldPath
$parent = dirname($path);
// Make sure there's a parent to return
if($parent === '.' || empty($parent))
return $default;
// Return the parent directory as Directory instance
return new Directory($parent);
}
/**
* Check whether the filesystem object has a parent directory.
* The root directory of the system doesn't have a parent directory, and thus will return false.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
*
* @return bool True if the object has a parent directory, false otherwise. False will also be returned on failure.
*/
public static function hasParent($path) {
// Convert the oldPath into a string, return false if failed
if(($path = self::asPath($path, false)) === null)
return false;
// Get the parent directory
$parent = trim(dirname($path));
// Check whether the parent directory is a real parent, return the result
return $parent === '.' || empty($parent);
}
/**
* Get the normalized oldPath of a filesystem object.
* This will remove unicode whitespaces and any kind of self referring or parent referring oldPath.
* The filesystem object doesn't need to exist.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string.
* @param mixed|null $default [optional] The default value returned on failure.
*
* @return string|null A normalized oldPath of the filesystem object,
*/
// TODO: Improve the quality of this method!
public static function getNormalizedPath($path, $default = null) {
// Convert the oldPath into a string, return the $default value if failed
if(($path = self::asPath($path, false)) === null)
return $default;
// Remove any kind of funky unicode whitespace
$normalized = preg_replace('#\p{C}+|^\./#u', '', $path);
// Path remove self referring oldPath ("/./").
$normalized = preg_replace('#/\.(?=/)|^\./|\./$#', '', $normalized);
// Regex for resolving relative oldPath
$regex = '#\/*[^/\.]+/\.\.#Uu';
while(preg_match($regex, $normalized))
$normalized = preg_replace($regex, '', $normalized);
// Check whether the oldPath is outside of the defined root oldPath
if(preg_match('#/\.{2}|\.{2}/#', $normalized))
return $default;
// Remove unwanted prefixed directory separators, return the result
$firstChar = substr($normalized, 0, 1);
if($firstChar === '\\' || $firstChar === '/')
return substr($normalized, 0, 1) . trim(substr($normalized, 1), '\\/');
return rtrim($normalized, '\\/');;
}
/**
* Get the absolute oldPath of a filesystem object. The filesystem object doesn't need to exist.
* A canonicalized version of the absolute oldPath will be returned if the filesystem object exists.
*
* @param FilesystemObject|string $path Filesystem object instance or oldPath to get the absolute oldPath for.
* @param mixed|null $default [optional] Default value to be returned on failure.
*
* @return string|mixed|null The absolute oldPath of the filesystem object, or the $default value if failed.
*/
// TODO: Improve the quality of this method!
public static function getAbsolutePath($path, $default = null) {
// Get the normalized oldPath, return the $default value if failed
if(($path = self::getNormalizedPath($path)) === null)
return $default;
// Try to get the real oldPath using PHPs function, return the result if succeed.
if(($realPath = realpath($path)) !== false)
return $realPath;
// Try to make the oldPath absolute without any system functions
// Check whether the oldPath is in unix format or not
$isUnixPath = empty($path) || $path{0} != '/';
// Detect whether the oldPath is relative, if so prefix the current working directory
if(strpos($path, ':') === false && $isUnixPath)
$path = getcwd() . DIRECTORY_SEPARATOR . $path;
// Put initial separator that could have been lost
$path = !$isUnixPath ? '/' . $path : $path;
// Resolve any symlinks
if(file_exists($path) && linkinfo($path) > 0)
$path = readlink($path);
// Return the result
return $path;
}
/**
* Get the canonical oldPath of a filesystem object. The filesystem object doesn't need to exist.
*
* @param FilesystemObject|string $path Filesystem object instance or oldPath to get the canonical oldPath for.
* @param mixed|null $default [optional] Default value to be returned on failure.
*
* @return string|mixed|null The canonicalized oldPath, or the $default value on failure.
*/
// TODO: Improve the quality of this method!
public static function getCanonicalPath($path, $default = null) {
// Convert the oldPath into a string, return the $default value if failed
if(($path = self::asPath($path, false)) === null)
return $default;
// Try to get the real oldPath using PHPs function, return the result if succeed.
if(($realPath = realpath($path)) !== false)
return $realPath;
// Try to canonicalize the oldPath even though it doesn't exist (Inspired by Sven Arduwie, Thanks!)
// Get the absolute oldPath and make sure it's valid
if(($path = self::getAbsolutePath($path)) === null)
return $default;
// Check whether the oldPath is in unix format or not
$isUnixPath = empty($path) || $path{0} != '/';
// Resolve all oldPath parts (single dot, double dot and double delimiters)
$path = str_replace(Array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = Array();
foreach($parts as $part) {
if('.' == $part)
continue;
if('..' == $part)
array_pop($absolutes);
else
$absolutes[] = $part;
}
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
// Put initial separator that could have been lost
if(!$isUnixPath)
$path = '/' . $path;
// Return the result
return $path;
}
/**
* Delete a filesystem object.
* Directories will only be deleted if they're empty unless the $recursive param is set to true.
*
* @param FilesystemObject|string $path Filesystem object instance or a oldPath string to delete.
* @param bool $recursive [optional] True to delete directories recursively. This option should be true if
* directories with contents should be deleted.
* @param resource $context [optional] See the unlink() function for documentation.
*
* @return int Number of deleted filesystem objects, a negative number will be returned on failure. This will also
* count the number of deleted recursive filesystem objects.
*
* @see unlink()
*/
// TODO: Add support for URI's
public static function delete($path, $recursive = false, $context = null) {
// Convert the oldPath into a string, return the $default value if failed
if(($path = self::asPath($path, false)) === null)
return -1;
// Count the deleted filesystem objects
$count = 0;
// Delete directory contents
if(self::isDirectory($path)) {
// Get the current filesystem object as directory instance
$dir = new Directory($path);
// If we need to delete the directory recursively, we need to delete the directory contents first
if($recursive)
$count += $dir->deleteContents($context);
// Delete the directory itself, and return the number of deleted filesystem objects
// TODO: Should this method be error muted?
$count += @rmdir($path, $context);
}
// Delete the filesystem object
// TODO: Should this method be error muted?
if(@unlink($path, $context))
$count++;
// Return the number of delete filesystem objects
return $count;
}
/**
* Rename a file or directory. The filesystem object must exist.
*
* @param FileSystemObject|string $oldPath The filesystem object instance or the oldPath of the filesystem object as
* a string.
* @param FileSystemObject|string $newPath The filesystem object instance of the oldPath of the filesystem object to
* rename the object to. This filesystem object or oldPath should include the full oldPath. The object may only exist if
* $overwrite is set to true or the renaming will fail.
* @param bool $overwrite [optional] True to overwrite the existing filesystem object when the newPath name already
* exist, false otherwise.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return bool True if the filesystem object was successfully renamed, false on failure.
*
* @see rename();
*/
// TODO: Allow simple renaming (without a full oldPath as new name) (simplify)
// TODO: Make sure this works with recursive directories!
// TODO: Add support for URI's
public static function rename($oldPath, $newPath, $overwrite = false, $context = null) {
// Convert $oldPath and $newPath into an absolute oldPath string, return false on failure
if(($oldPath = self::getAbsolutePath($oldPath)) === null)
return false;
if(($newPath = self::getAbsolutePath($newPath)) === null)
return false;
// Make sure the old oldPath exists, and the new oldPath is valid
if(!self::exists($oldPath) || !self::isValid($newPath))
return false;
// Make sure the new oldPath may be overwritten if it already exists
if(self::exists($newPath) & !$overwrite)
return false;
// Check whether the old oldPath is a symbolic link
if(self::isSymbolicLink($oldPath)) {
// Delete the filesystem object that will be overwritten before renaming the object
if(FilesystemObjectHelper::exists($newPath))
if(FilesystemObjectHelper::delete($newPath, true) <= 0)
return false;
// Read the newPath of the symlink
$link = SymbolicLink::asSymbolicLink($oldPath);
// Create the new symbolic link, and make sure it was made successfully
$newLink = SymbolicLinkHelper::createSymbolicLink($link->getTarget(), $newPath);
if($newLink == null)
return false;
// Delete the old symbolic link, return the result
return $link->delete() > 0;
}
// Rename the object, return the result
if($context !== null)
return rename($oldPath, $newPath, $context);
return rename($oldPath, $newPath);
}
/**
* Move a file system object. The filesystem object that should be moved must exist.
*
* @param FileSystemObject|string $source The filesystem object instance or oldPath of the filesystem object as a string.
* The filesystem object must exist.
* @param FileSystemObject|string $destination The filesystem object instance or the oldPath of a the filesystem object to
* move the object to. This filesystem object or oldPath should be a full/absolute oldPath.
* @param bool $overwrite True to overwrite the newPath with the oldPath if the newPath already exists.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return bool True on success, false on failure.
*
* @see rename();
*/
// TODO: Make sure this works with recursive directories!
public static function move($source, $destination, $overwrite = true, $context = null) {
// Convert $oldPath and $newPath into a oldPath string, return false on failure
if(($source = self::asPath($source, false)) === null)
return false;
if(($destination = self::asPath($destination, false)) === null)
return false;
// Validate the oldPath and newPath
if(!self::exists($source) || !self::isValid($destination))
return false;
// Rename/move the oldPath, return the result
return self::rename($source, $destination, $overwrite, $context);
}
/**
* Rename a file or directory. The filesystem object must exist.
*
* @param FileSystemObject|string $source The filesystem object instance or the oldPath of the filesystem object as
* a string.
* @param FileSystemObject|string $destination The filesystem object instance of the oldPath of the filesystem object to
* rename the object to. This filesystem object or oldPath should include the full oldPath. The object may only exist if
* $overwrite is set to true or the renaming will fail.
* @param bool $overwrite [optional] True to overwrite the existing filesystem object when the newPath name already
* exist, false otherwise.
* @param resource $context [optional] See the rename() function for documentation.
*
* @return bool True if the filesystem object was successfully renamed, false on failure.
*
* @see rename();
*/
// TODO: Allow simple copying(without a full oldPath as new name) (simplify)
// TODO: Make sure this works with recursive directories!
// TODO: Add support for URI's
public static function copy($source, $destination, $overwrite = false, $context = null) {
// Convert $oldPath and $newPath into an absolute oldPath string, return false on failure
if(($source = self::getAbsolutePath($source)) === null)
return false;
if(($destination = self::getAbsolutePath($destination)) === null)
return false;
// Make sure the old oldPath exists, and the new oldPath is valid
if(!self::exists($source) || !self::isValid($destination))
return false;
// Make sure the new oldPath may be overwritten if it already exists
if(self::exists($destination) & !$overwrite)
return false;
// Check whether a symbolic link must be copied
if(self::isSymbolicLink($source)) {
// Delete the filesystem object that will be overwritten before copying the object
if(FilesystemObjectHelper::exists($destination))
if(FilesystemObjectHelper::delete($destination, true) <= 0)
return false;
// Read the newPath of the symlink
$link = SymbolicLink::asSymbolicLink($source);
// Create the new symbolic link, and make sure it was made successfully
return SymbolicLinkHelper::createSymbolicLink($link->getTarget(), $destination) !== null;
}
// Copy the object, return the result
if($context !== null)
return copy($source, $destination, $context);
return copy($source, $destination);
}
/**
* Validate a filesystem object instance or filesystem object oldPath as a string. The filesystem object doesn't need
* to exist to be valid.
*
* @param FilesystemObject|string $path The filesystem object instance, or the oldPath as a string to validate.
*
* @return bool True if the filesystem object instance of the filesystem object oldPath is valid, false otherwise.
*/
// TODO: Improve method quality!
public static function isValid($path) {
// Convert $oldPath into a oldPath, return false on failure
if(($path = self::asPath($path)) === null)
return false;
// Trim the oldPath, and make sure the oldPath is set
$path = @trim($path);
if(empty($path))
return false;
// TODO: Use better validation for $oldPath!
// The oldPath seems to be valid, return the result
return true;
}
// TODO: Implement $context usage better, possibly optionally with a function instead of using function parameters!
}
<?php
namespace carbon\core\io\filesystem;
use carbon\core\io\filesystem\directory\Directory;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class FilesystemObjectProvider {
/**
* Get the Carbon Core root directory.
*
* @return Directory Carbon Core root directory.
*/
public static function getCarbonCoreDirectory() {
return new Directory(CARBON_CORE_ROOT);
}
/**
* Get the systems root directory.
*
* @return Directory Systems root directory.
*/
public static function getRootDirectory() {
return new Directory('/');
}
/**
* Get PHPs current working directory.
*
* @return Directory Returns PHPs current working directory.
*/
public static function getCurrentWorkingDirectory() {
return new Directory(getcwd());
}
/**
* Get the home directory for the current user.
* Warning: This method is only compatible with a few systems thus the default value is often returned.
*
* @param mixed|null $default [optional] The default value to be returned on failure.
*
* @return Directory|null|string The home directory of the current user, or the $default value on failure.
*/
public static function getHomeDirectory($default = null) {
// Check whether the home path on linux/unix systems is available in the server variables
if(isset($_SERVER['HOME']))
return new Directory($_SERVER['HOME']);
// Get the home from the environment variables
if(($home = getenv('HOME')) !== false)
return new Directory($home);
// Check whether the home path on windows system is available in the server variables
if(!empty($_SERVER['HOMEDRIVE']) && !empty($_SERVER['HOMEPATH']))
return $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
// Home path couldn't be determined, return the default value
return $default;
}
}
<?php
namespace carbon\core\io\filesystem\permissions;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
// TODO: Implement a lot more features in this class!
class SystemGroup {
/** @var int Group ID */
private $gid;
/**
* Constructor
*
* @param int|SystemGroup $gid Group ID
*/
// TODO: Support group names as argument
public function __construct($gid) {
$this->gid = $gid;
}
/**
* Get the group ID
*
* @return int Group ID
*/
public function getId() {
return $this->gid;
}
}
<?php
namespace carbon\core\io\filesystem\permissions;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
// TODO: Implement a lot more features in this class!
class SystemUser {
/** @var int User ID */
private $uid;
/**
* Constructor
*
* @param int $uid User ID
*/
// TODO: Support user names as argument
public function __construct($uid) {
$this->uid = $uid;
}
/**
* Get the user ID
*
* @return int User ID
*/
public function getId() {
return $this->uid;
}
}
<?php
/**
* URI.php
* The URI class, which is used to manage URI's.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
use carbon\core\cache\simplecache\SimpleCache;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URI class.
*
* @package carbon\core\io
* @author Tim Visee
*/
class URI {
/** @var string|null The scheme of the URI or null. */
protected $scheme;
/** @var string|null The host of the URI or null. */
protected $host;
/** @var int|null The port of the URI or null. */
protected $port;
/** @var URIUserInfo|null The user info of the URI or null. */
protected $userInfo;
/** @var string|null The path of the URI or null. */
protected $path;
/** @var string|null The query of the URI or null. */
protected $query;
/** @var string|null The fragment of the URI or null. */
protected $fragment;
// TODO: Should we remove the cache!?
/** @var SimpleCache Instance used for basic caching. */
protected $cache;
/**
* Constructor.
*
* @param string|null $scheme The URI scheme or null.
* @param string|null $host The URI host or null.
* @param int|null $port The URI port or null.
* @param URIUserInfo|null $userInfo The URI user info or null.
* @param string|null $path The URI path or null.
* @param string|null $query The URI query or null.
* @param string|null $fragment The URI fragment or null.
*/
public function __construct($scheme, $host, $port, $userInfo, $path, $query, $fragment) {
// Initialize the simple cache
$this->cache = new SimpleCache();
// Set the URI segments
$this->scheme = $scheme;
$this->host = $host;
$this->port = $port;
$this->userInfo = $userInfo;
$this->path = $path;
$this->query = $query;
$this->fragment = $fragment;
}
/**
* Parse an URI.
*
* @param string|URI $uri The URI to parse as a string or as URI instance.
* @param bool $validate [optional] True to validate the URI, false to skip validation. If the URI isn't valid null
* will be returned.
* @return URI|null The parsed URI as URI instance, or null on failure.
*/
public static function parse($uri, $validate = false) {
return URIHelper::parseURI($uri, $validate, null);
}
/**
* Flush the cache.
*
* @return bool True on success, false on failure.
*/
public function flushCache() {
return $this->cache->flush() >= 0;
}
/**
* Get the scheme of the URI if set.
*
* @return string|null The scheme of the URI or null.
*/
public function getScheme() {
return $this->scheme;
}
/**
* Set the scheme of the URI.
*
* @param string|null $scheme The scheme of the URI or null.
*/
public function setScheme($scheme) {
$this->scheme = $scheme;
}
/**
* Check whether the URI has any scheme set.
*
* @return bool True if the URI has any scheme set, false if not.
*/
public function hasScheme() {
return ($this->scheme !== null);
}
/**
* Get the host of the URI if set.
*
* @return string|null The host of the URI or null.
*/
public function getHost() {
return $this->host;
}
/**
* Set the host of the URI.
*
* @param string|null $host The host of the URI or null.
*/
public function setHost($host) {
$this->host = $host;
}
/**
* Check whether the URI has any host set.
*
* @return bool True if the URI has any host set, false if not.
*/
public function hasHost() {
return ($this->host !== null);
}
/**
* Get the port of the URI if set.
*
* @return int|null The port of the URI or null.
*/
public function getPort() {
return $this->port;
}
/**
* Set the port of the URI.
*
* @param int|null $port The port of the URI or null.
*/
public function setPort($port) {
$this->port = $port;
}
/**
* Check whether the URI has any port set.
*
* @return bool True if the URI has any port set, false if not.
*/
public function hasPort() {
return ($this->port !== null);
}
/**
* Get the user info of the URI if set.
*
* @return URIUserInfo|null The user info of the URI or null.
*/
public function getUserInfo() {
return $this->userInfo;
}
/**
* Set the user info of the URI.
*
* @param URIUserInfo|null $userInfo The user info of the URI or null.
*/
public function setUserInfo($userInfo) {
$this->userInfo = $userInfo;
}
/**
* Check whether the URI has any user info set.
*
* @return bool True if the URI has any user info set, false if not.
*/
public function hasUserInfo() {
// Make sure an user info instance is available
if($this->userInfo === null)
return false;
// Make sure any user info is set, return the result
return $this->userInfo->hasUserInfo();
}
/**
* Get the path of the URI if set.
*
* @return string|null The path of the URI or null.
*/
public function getPath() {
return $this->path;
}
/**
* Set the path of the URI.
*
* @param string|null $path The path of the URI or null.
*/
public function setPath($path) {
$this->path = $path;
}
/**
* Check whether the URI has any scheme set.
*
* @return bool True if the URI has any path set, false if not.
*/
public function hasPath() {
return ($this->path !== null);
}
/**
* Get the query of the URI if set.
*
* @return string|null The query of the URI or null.
*/
public function getQuery() {
return $this->query;
}
/**
* Set the query of the URI.
*
* @param string|null $query The query of the URI or null.
*/
public function setQuery($query) {
$this->query = $query;
}
/**
* Check whether the URI has any query set.
*
* @return bool True if the URI has any query set, false if not.
*/
public function hasQuery() {
return ($this->query !== null);
}
/**
* Get the fragment of the URI if set.
*
* @return string|null The fragment of the URI or null.
*/
public function getFragment() {
return $this->fragment;
}
/**
* Set the fragment of the URI.
*
* @param string|null $fragment The fragment of the URI or null.
*/
public function setFragment($fragment) {
$this->fragment = $fragment;
}
/**
* Check whether the URI has any fragment set.
*
* @return bool True if the URI has any fragment set, false if not.
*/
public function hasFragment() {
return ($this->fragment !== null);
}
/**
* Make sure the URI is valid.
*
* @return bool True if the URI is valid, false otherwise.
*/
public function isValid() {
return URIHelper::isValid($this);
}
/**
* Get the URI without any formatting as a string.
*
* @return string|null The URI as a string. Null will be returned on failure.
*/
public function getURIString() {
return URIHelper::getURIString($this, null);
}
/**
* Get the URI as a string.
*
* @return string The URI as a string. An empty string will be returned on failure.
*/
public function toString() {
// Get the URI as a string, make sure it's valid
$uri = $this->getURIString();
if($uri === null)
return '';
// Return the URI string
return $uri;
}
/**
* Get the URI as a string.
*
* @return string The URI as a string. An empty string will be returned on failure.
*/
public function __toString() {
return $this->toString();
}
}
<?php
/**
* URIFactory.php
* The URI factory class, which is used to fabricate URI's.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class URIFactory {
/** @var string|null Defines the URI scheme as a string, or null if unavailable. */
private $scheme = null;
/** @var string|null Defines the URI host as a string, or null if unavailable. */
private $host;
/** @var int|null Defines the URI port as an int, or null if unavailable. */
private $port = null;
/** @var string|null Defines the URI user as a string, or null if unavailable. */
private $user = null;
/** @var string|null Defines the URI password as a string, or null if unavailable. */
private $pass = null;
/** @var string|null Defines the URI path as a string, or null if unavailable. */
private $path = null;
/** @var string|null Defines the URI query as a string, or null if unavailable. */
private $query = null;
/** @var string|null Defines the URI fragment as a string, or null if unavailable. */
private $fragment = null;
// TODO: Userinfo such as user:pass should be put together?
/**
* Constructor.
*/
public function __construct($uri) {
// TODO: Complete this method!
}
/**
* Get the URI scheme if set.
*
* @return string|null The scheme of the URI if available, null otherwise.
*/
public function getScheme() {
return $this->scheme;
}
/**
* Set the URI scheme.
*
* @param string|URI|null $scheme The URI scheme as a string. Or an URI instance to copy the URI scheme.
* Null to remove the scheme.
*
* @return bool True on success, false on failure.
*/
public function setScheme($scheme) {
// Get the scheme form an URI instance
if($scheme instanceof URI)
$scheme = $scheme->getScheme();
// Make sure the scheme is a string or null
if(!is_string($scheme) && $scheme !== null)
return false;
// Set the scheme, return the result
$this->scheme = $scheme;
return true;
}
/**
* Check whether the URI has a scheme set.
*
* @return bool True if a scheme is set, false otherwise.
*/
public function hasScheme() {
return $this->scheme !== null;
}
/**
* Get the URI host.
*
* @return string The host of the URI.
*/
public function getHost() {
return $this->host;
}
/**
* Set the URI host.
*
* @param string|URI $host The URI host as a string. Or an URI instance to copy the URI host.
*
* @return bool True on success, false on failure.
*/
public function setHost($host) {
// Get the host from an URI instance
if($host instanceof URI)
$host = $host->getHost();
// Make sure the host is a string
if(!is_string($host))
return false;
// Set the host, return the result
$this->host = $host;
return true;
}
/**
* Get the URI port if set.
*
* @return int|null
*/
public function getPort() {
return $this->port;
}
/**
* Set the URI port.
*
* @param int|string|null $port The URI port number as an integer or string. Or an URI instance to copy the URI port.
*
* @return bool True on success, false on failure.
*/
public function setPort($port) {
// Get the port form an URI instance
if($port instanceof URI)
$port = $port->getPort();
// Try to convert the port into an integer if it's a string
if(is_string($port)) {
// Cast the port number,
$port = intval($port);
// Make sure the port number is valid
if(!URIHelper::isValidPort($port))
return false;
}
// Make sure the port number is an integer or null
if(!is_int($port) && $port !== null)
return false;
// Set the port number, return the result
$this->port = $port;
return true;
}
/**
* Check whether the URI has a port set.
*
* @return bool True if a port is set, false otherwise.
*/
public function hasPort() {
return $this->port !== null;
}
/**
* Get the URI user if set.
*
* @return string|null The user of the URI if available, null otherwise.
*/
public function getUser() {
return $this->user;
}
/**
* Set the URI user.
*
* @param string|URI|null $user The URI user as a string. Or an URI instance to copy the URI user.
* Null to remove the user.
*
* @return bool True on success, false on failure.
*/
public function setUser($user) {
// Get the user from an URI instance
if($user instanceof URI)
$user = $user->getUserInfo();
// Make sure the user is a string or null
if(!is_string($user) && $user !== null)
return false;
// Set the user, return the result
$this->user = $user;
return true;
}
/**
* Check whether the URI has a user set.
*
* @return bool True if a user is set, false otherwise.
*/
public function hasUser() {
return $this->user !== null;
}
/**
* Get the URI password if set.
*
* @return string|null The password of the URI if available, null otherwise.
*/
public function getPassword() {
return $this->pass;
}
/**
* Set the URI password.
*
* @param string|URI|null $pass The URI password as a string. Or an URI instance to copy the URI password.
* Null to remove the password.
*
* @return bool True on success, false on failure.
*/
public function setPassword($pass) {
// Get the password from an URI instance
if($pass instanceof URI)
$pass = $pass->getPassword();
// Make sure the password is a string or null
if(!is_string($pass) && $pass !== null)
return false;
// Set the password, return the result
$this->pass = $pass;
return true;
}
/**
* Check whether the URI has a pass set.
*
* @return bool True if a pass is set, false otherwise.
*/
public function hasPassword() {
return $this->pass !== null;
}
/**
* Get the URI path if set.
*
* @return string|null The path of the URI if available, null otherwise.
*/
public function getPath() {
return $this->path;
}
/**
* Set the URI path.
*
* @param string|URI|null $path The URI path as a string. Or an URI instance to copy the URI path.
* Null to remove the path.
*
* @return bool True on success, false on failure.
*/
public function setPath($path) {
// Get the path from an URI instance
if($path instanceof URI)
$path = $path->getPath();
// Make sure the path is a string or null
if(!is_string($path) && $path !== null)
return false;
// Set the path, return the result
$this->path = $path;
return true;
}
/**
* Check whether the URI has a path set.
*
* @return bool True if a path is set, false otherwise.
*/
public function hasPath() {
return $this->path !== null;
}
/**
* Get the URI query if set.
*
* @return string|null The query of the URI if available, null otherwise.
*/
public function getQuery() {
return $this->query;
}
/**
* Set the URI query.
*
* @param string|URI|null $query The URI query as a string. Or an URI instance to copy the URI query.
* Null to remove the path.
*
* @return bool True on success, false on failure.
*/
// TODO: More query options!
public function setQuery($query) {
// Get the query from an URI instance
if($query instanceof URI)
$query = $query->getQuery(false);
// Make sure the query is a string or null
if(!is_string($query) && $query !== null)
return false;
// Set the query, return the result
$this->query = $query;
return true;
}
/**
* Check whether the URI has a query set.
*
* @return bool True if a query is set, false otherwise.
*/
public function hasQuery() {
return $this->query !== null;
}
/**
* Get the URI fragment if set.
*
* @return string|null The fragment of the URI if available, null otherwise.
*/
public function getFragment() {
return $this->fragment;
}
/**
* Set the URI fragment.
*
* @param string|URI|null $fragment The URI fragment as a string. Or an URI instance to copy the URI fragment.
* Null to remove the path.
*
* @return bool True on success, false on failure.
*/
public function setFragment($fragment) {
// Get the query from the URI instance
if($fragment instanceof URI)
$fragment = $fragment->getFragment();
// Make sure the fragment is a string or null
if(!is_string($fragment) && $fragment !== null)
return false;
// Set the fragment, return the result
$this->fragment = $fragment;
return true;
}
/**
* Check whether the URI has a fragment set.
*
* @return bool True if a fragment is set, false otherwise.
*/
public function hasFragment() {
return $this->fragment !== null;
}
/**
* Get the URI.
*
* @return string
*/
// TODO: Update this method!
public function getUri() {
// Define the URI
$uri = '';
// Append the scheme
if($this->hasScheme())
$uri .= $this->getScheme() . ':';
// Append the username and password
if($this->hasUser()) {
// Append the user
$uri .= $this->getUser();
// Append the password
if($this->hasPassword())
$uri .= ':' . $this->getPassword();
$uri .= '@';
}
// Append the host
$uri .= $this->getHost();
// Append the port
if($this->hasPort())
$uri .= ':' . $this->getPort();
// Append the path
if($this->hasPath())
$uri .= $this->getPath();
// Append the query
if($this->hasQuery())
$uri .= '?' . $this->getQuery();
// Append the fragment
if($this->hasFragment())
$uri .= '#' . $this->getFragment();
// Return the URI
return $uri;
}
}
<?php
/**
* URIHelper.php
* The URI helper class, which is used to process URI's.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URI utils class.
*
* @package carbon\core\io
* @author Tim Visee
*/
class URIHelper {
// TODO: Use scheme objects, instead of strings?
// TODO: Create an object to get the current page request URI data from?
/**
* Parse an URI.
* Invalid characters are replaced by an underscore sign.
*
* The following segments of an URI will be parsed:
* - scheme (the used protocol, e.g. http)
* - host
* - port
* - user
* - pass
* - path
* - query (after the question mark ?)
* - fragment (after the hashmark #)
*
* @param string|URI $uri The URI to parse as a string or as URI instance.
* @param bool $validate [optional] True to validate the URI, false to skip validation. The default value will be
* returned if the URI isn't valid.
* @param mixed $default [optional] The default value returned on failure.
*
* @return URI|mixed The parsed URI as URI instance, or the default value on failure.
*/
// TODO: Parse caching?
public static function parseURI($uri, $validate = false, $default = null) {
// Return the URI if it's an URI instance already
if($uri instanceof URI)
return $uri;
// Parse the URI, and make sure it's valid
$parsed = parse_url($uri);
if($parsed === false)
return $default;
// Parse the user info
$userInfo = null;
if(isset($parsed['user']) || isset($parsed['pass']))
$userInfo = new URIUserInfo(@$parsed['user'], @$parsed['pass']);
// Parse the URI
$uri = new URI(
@$parsed['scheme'],
@$parsed['host'],
@$parsed['port'],
$userInfo,
@$parsed['path'],
@$parsed['query'],
@$parsed['fragment']
);
// Make sure the URI is valid
if($validate)
if(!$uri->isValid())
return $default;
// Return the parsed URI
return $uri;
}
/**
* Get an URI as a string.
*
* @param URI|string $uri The URI to get as an URI instance or as a string.
* @param bool $validate [optional] True to validate the URI.
* @param mixed $default [optional] The default value returned if the URI couldn't be parsed.
*
* @return string|mixed The URI as a string. The default value will be returned if the URI couldn't be parsed.
* If $validate is set to true, the URI must be valid or the default value will be returned.
*/
public static function parseURIString($uri, $validate = false, $default = null) {
// Parse the URI, and make sure it parsed successfully
if(($uri = self::parseURI($uri, $validate, null)) === null)
return $default;
// Get the URI string, make sure it's valid
$uriString = $uri->getURIString();
if($uriString === null)
return $default;
// Return the URI string
return $uriString;
}
/**
* Get the scheme of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the scheme from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The scheme of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getScheme($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the scheme, or the default value if
if($uri->hasScheme())
return $uri->getScheme();
return $default;
}
/**
* Get the host of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the host from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The host of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getHost($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the host, or the default value if
if($uri->hasHost())
return $uri->getHost();
return $default;
}
/**
* Get the port of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the port from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return int|mixed The port of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getPort($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the port, or the default value if
if($uri->hasPort())
return $uri->getPort();
return $default;
}
/**
* Get the user info of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the user info from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return URIUserInfo|mixed The user info of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getUserInfo($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the user info, or the default value if
if($uri->hasUserInfo())
return $uri->getUserInfo();
return $default;
}
/**
* Get the path of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the path from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The path of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getPath($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the path, or the default value if
if($uri->hasPath())
return $uri->getPath();
return $default;
}
/**
* Get the query of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the query from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The query of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getQuery($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the query, or the default value if
if($uri->hasQuery())
return $uri->getQuery();
return $default;
}
/**
* Get the fragment of an URI if set.
*
* @param URI|string $uri The URI as URI instance or as string to get the fragment from.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The fragment of the URI if set, the default value otherwise. The default value will also be
* returned on failure.
*/
public static function getFragment($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Return the fragment, or the default value if
if($uri->hasFragment())
return $uri->getFragment();
return $default;
}
/**
* Get an URI as an URI string.
*
* @param URI|string $uri The URI as URI instance or as string to get as URI string.
* @param mixed $default [optional] The default value returned on failure.
*
* @return string|mixed The URI as a string or the default value on failure.
*/
public static function getURIString($uri, $default = null) {
// Parse the URI, and make sure it's parsed successfully
if(($uri = self::parseURI($uri, false, null)) === null)
return $default;
// Reconstruct the authority
$authority = null;
if($uri->hasScheme()) {
$authority = '';
// Append the user info if set
if($uri->hasUserInfo())
$authority .= $uri->getUserInfo() . '@';
// Append the host
$authority .= $uri->getHost();
// Append the port if set
if($uri->hasPort())
$authority .= ':' . $uri->hasPort();
}
// Reconstruct the URI
$uriString = '';
// Append the scheme if set
if($uri->hasScheme())
$uriString .= $uri->getScheme() . ':';
// Append the authority if available
if($authority !== null)
$uriString .= '//' . $authority;
// Append the path
$uriString .= $uri->getPath();
// Append the query
if($uri->hasQuery())
$uriString .= '?' . $uri->getQuery();
// Append the fragment
if($uri->hasFragment())
$uriString .= '#' . $uri->getFragment();
// Return the reconstructed URI
return $uriString;
}
/**
* Validate an URI
*
* @param URI|string $uri The URI to validate as an URI instance or string.
*
* @return bool True if the URI is valid, false otherwise.
*/
public static function isValid($uri) {
// Get the URI as a string and make sure it's valid
if(($uri = self::parseURIString($uri, false, null)) === null)
return false;
// TODO: Use a different (better performing) validation method
// Check whether the URI is valid, return the result
return filter_var($uri, FILTER_VALIDATE_URL) !== false;
}
}
<?php
/**
* URI.php
* The URI class, which is used to manage URI's.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URI user info class.
*
* @package carbon\core\io
* @author Tim Visee
*/
class URIUserInfo {
/** @var string|null The user of the URI or null. */
protected $user;
/** @var string|null The password of the URI or null. */
protected $password;
/**
* Constructor.
*
* @param string|null $user The URI user or null.
* @param string|null $password The URI password or null.
*/
public function __construct($user, $password) {
// Set the URI segments
$this->user = $user;
$this->password = $password;
}
/**
* Get the user of the URI if set.
*
* @return string|null The user of the URI or null.
*/
public function getUser() {
return $this->user;
}
/**
* Set the user of the URI.
*
* @param string|null $user The user of the URI or null.
*/
public function setUser($user) {
$this->user = $user;
}
/**
* Check whether the URI has any user set.
*
* @return bool True if the URI has any user set, false if not.
*/
public function hasUser() {
return ($this->user !== null);
}
/**
* Get the password of the URI if set.
*
* @return string|null The password of the URI or null.
*/
public function getPassword() {
return $this->password;
}
/**
* Set the password of the URI.
*
* @param string|null $password The password of the URI or null.
*/
public function setPassword($password) {
$this->password = $password;
}
/**
* Check whether the URI has any password set.
*
* @return bool True if the URI has any password set, false if not.
*/
public function hasPassword() {
return ($this->password !== null);
}
/**
* Get the user info as a string.
*
* @return string The user info as a string, false on failure.
*/
public function getUserInfo() {
// Make sure there's any user info
if(!$this->hasUserInfo())
return null;
// Build the user info string
$userInfo = '';
// Set the user
if($this->hasUser())
$userInfo .= $this->getUser();
// Set the password
if($this->hasPassword())
$userInfo .= ':' . $this->getPassword();
// Return the user info
return $userInfo;
}
/**
* Check whether this instance has any user info set.
*
* @return bool True if any user info was set, false if not.
*/
public function hasUserInfo() {
return $this->hasUser() || $this->hasPassword();
}
/**
* Get the user info as a string.
*
* @return string The user info, or an empty string if no user info was set.
*/
public function toString() {
// Get the user info, make sure it's valid
$userInfo = $this->getUserInfo();
if($userInfo === null)
return '';
// Return the user info
return $userInfo;
}
/**
* Get the user info as a string.
*
* @return string The user info, or an empty string if no user info was set.
*/
public function __toString() {
return $this->toString();
}
}
<?php
/**
* URL.php
* The URL class, which is used to manage URL's.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URL class.
*
* @package carbon\core\io\uri
* @author Tim Visee
*/
class URL extends URI { }
<?php
/**
* URIUserInfo.php
* The URI user info class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URI user info class.
*
* @package carbon\core\io\uri\userinfo
* @author Tim Visee
*/
class URIUserInfo {
/** @var string|null The user info. */
private $info;
/**
* Constructor.
*
* @param string|null $userInfo The user info to set, or null.
*/
public function __construct($userInfo) {
// Set the user info
// TODO: Add some error validation!?
$this->setUserInfo($userInfo, false);
}
/**
* Get the user info as a string.
*
* @param bool $validate [optional] True to validate the user info, false to skip the validation.
*
* @return string|null The user info as a string, or null if not user info was set. Null will also be returned if
* the user info isn't valid when validation is used.
*/
public function getUserInfo($validate = false) {
// Validate the user info if needed
if($validate)
if(!$this->isValid())
return null;
// Get the user info
$userInfo = $this->toString();
// Return the user info
if(strlen($userInfo) == 0)
return null;
return $userInfo;
}
/**
* Set the user info.
*
* @param string|URIUserInfo|null $userInfo The user info as a string, or as an user info instance. Null to reset
* the user info.
* @param bool $validate [optional] True to make sure the user info is valid before it's set, false otherwise.
* Null will also count as valid user info since this resets the user info.
*
* @return bool True if the user info was set, false otherwise. True will also be returned if the user info was
* reset.
*/
public function setUserInfo($userInfo, $validate = false) {
// Check whether the user info should be reset
if($userInfo === null) {
$this->info = null;
return true;
}
// Get the user info as a string, and make sure it's valid
if(($userInfo = URIUserInfoHelper::asString($userInfo, $validate, null)) === null)
return false;
// Set the user info, return the result
$this->info = $userInfo;
return true;
}
/**
* Get the user info as a string.
*
* @return string The user info as a string. An empty string will be returned if no user info is set.
*/
public function toString() {
// Make sure the user info is set
if($this->info === null)
return '';
// Return the user info
return $this->info;
}
/**
* Validate the user info.
*
* @return bool True if the user info is valid, false otherwise.
*/
public function isValid() {
return URIUserInfoHelper::isValid($this);
}
}
<?php
/**
* URIUserInfoHelper.php
* The URI user info utilities class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (c) Tim Visee 2015. All rights reserved.
*/
namespace carbon\core\io\uri;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* URI user info utilities class.
*
* @package carbon\core\io\uri\userinfo
* @author Tim Visee
*/
class URIUserInfoHelper {
/**
* Character classes defined in RFC-3986
*/
const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
const CHAR_GEN_DELIMS = ':\/\?#\[\]@';
const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
const CHAR_RESERVED = ':\/\?#\[\]@!\$&\'\(\)\*\+,;=';
/**
* Not in the spec - those characters have special meaning in urlencoded query parameters
*/
const CHAR_QUERY_DELIMS = '!\$\'\(\)\*\,';
/**
* Host part types represented as binary masks
* The binary mask consists of 5 bits in the following order:
* <RegName> | <DNS> | <IPvFuture> | <IPv6> | <IPv4>
* Place 1 or 0 in the different positions for enable or disable the part.
* Finally use a hexadecimal representation.
*/
const HOST_IPV4 = 0x01; //00001
const HOST_IPV6 = 0x02; //00010
const HOST_IPVFUTURE = 0x04; //00100
const HOST_IPVANY = 0x07; //00111
const HOST_DNS = 0x08; //01000
const HOST_DNS_OR_IPV4 = 0x09; //01001
const HOST_DNS_OR_IPV6 = 0x0A; //01010
const HOST_DNS_OR_IPV4_OR_IPV6 = 0x0B; //01011
const HOST_DNS_OR_IPVANY = 0x0F; //01111
const HOST_REGNAME = 0x10; //10000
const HOST_DNS_OR_IPV4_OR_IPV6_OR_REGNAME = 0x1B; //11011
const HOST_ALL = 0x1F; //11111
/**
* Get some user info as a string.
*
* @param URIUserInfo|string $userInfo The user info as user info instance or as a string.
* @param bool $validate [optional] True to validate the user info, false to skip validation.
* @param mixed|null $default [optional] The default value returned on error.
*
* @return string|mixed|null The user info as a string. The default value will be returned if the URI is invalid
* while validation is enabled, or on error.
*/
public static function asString($userInfo, $validate = false, $default = null) {
// Make sure the user info isn't null
if($userInfo === null)
return $default;
// Convert user info instances into a string
if($userInfo instanceof URIUserInfo)
return $userInfo->getUserInfo($validate);
// Make sure the user info is a string
if(!is_string($userInfo))
return $default;
// Validate the user info
if($validate)
if(!self::isValid($userInfo))
return $default;
// Return the user info
return $userInfo;
}
/**
* Validate URI user info.
*
* @param URIUserInfo|string $userInfo The user info as user info instance or as a string.
*
* @return bool True if the user info is valid, false otherwise.
*/
public static function isValid($userInfo) {
// Get the user info as a string, and make sure it's valid
if(($userInfo = self::asString($userInfo, false, null)) === null)
return false;
// Validate the user info using regex
$regex = '/^(?:[' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ':]+|%[A-Fa-f0-9]{2})*$/';
return preg_match($regex, $userInfo) != 0;
}
}
<?php
/**
* Language.php
*
* Language class.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language;
use carbon\core\exception\language\CarbonUnknownLanguageException;
use carbon\core\language\util\LanguageUtils;
use carbon\core\util\LocaleUtils;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Language class.
*
* @package core\langauge
* @author Tim Visee
*/
class Language {
/** @var string $dir Directory being used by this language */
private $dir;
/** @var string $locales LanguageTag tags this langauge could be used for */
private $locales;
/** @var string $name Display name of the language */
private $name;
/** @var string $desc Language description */
private $desc;
/** @var LanguageManifest $manifest Language manifest */
private $manifest;
/**
* Constructor
* @param string $dir Directory of this language
* @param LanguageManifest $manifest Language manifest
* The canonical language tag will be used if a Language or LanguageTag instance is supplied.
* @throws CarbonUnknownLanguageException Throws exception when an invalid language is supplied
*/
public function __construct($dir, $manifest) {
// Get the language if the lang param is an instance of a LanguageTag
// TODO: What is this $lang tag for?
if($lang instanceof LanguageTag)
$lang = $lang->getLanguage();
// Get the language tag if the lang param is an instance of Language
if($lang instanceof self)
$lang = LanguageUtils::getCanonicalTag($lang->getTag());
// Make sure the lang param isn'elapsed null or an empty string
if(empty($lang)) {
throw new CarbonUnknownLanguageException(
'Invalid language supplied while constructing Language class.',
0,
null
);
}
// Store the language
$this->lang = trim($lang);
}
/**
* Get the language tag
* @return string Language tag
*/
public function getTag() {
return $this->lang;
}
/**
* Get the language tag
* @return string Language tag
*/
public function getLanguageTag() {
// Check whether the locales contains a underscore
if(!StringUtils::contains($this->locale, '_'))
return $this->locale;
// Split the locales into it'statements parts
$locale_parts = explode('_', $this->locale, 2);
// Return the language tag
return $locale_parts[0];
}
/**
* Get all locales for this language
* @return array|null List of locales for this language, null if the language was invalid
*/
public function getLocales() {
return LocaleUtils::getLocalesOfLanguage($this->lang);
}
/**
* Check whether the language tag is valid
* @return bool True when the language seems to be valid, false otherwise
*/
public function isValid() {
return LanguageUtils::isValidLanguage($this->lang);
}
}
<?php
/**
* LocaleFile.php
*
* language set_file class.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\locale;
use carbon\core\exception\language\CarbonLanguageFileLoadException;
use carbon\core\exception\language\CarbonLocaleException;
use carbon\core\language\LanguageTag;
use carbon\core\util\LocaleUtils;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* language set_file class.
*
* @package core\langauge
* @author Tim Visee
*/
class LocaleFile {
/** @var string $set_file File path to the language set_file */
private $file;
/** @var Locale $locale LanguageTag of this language set_file */
private $locale;
/** @var array|null $data LanguageTag data */
private $data = null;
// TODO: Rename scope to namespace?
/** @var string $scope language set_file scope */
private $scope = "";
/** @var array|null $props language set_file properties */
private $props = null;
/** @var array $scope_cache Cache used to remember whether this set_file is included in a scope */
private $scope_cache = Array();
/**
* Constructor
* @param string $lang_file Path of the language set_file to load
* @param Locale $lang LanguageTag of the set_file
* @param bool $throw_exceptions True to throw exceptions if failed to load the language set_file
* @throws CarbonLanguageFileLoadException Throws an exception if failed to load the language set_file,
* $throw_exceptions has to be true.
*/
public function __construct($lang_file, $lang, $throw_exceptions = true) {
// Set the lang_tag of the set_file
$this->locale = $lang;
// Load the language set_file
$this->loadFile($lang_file, $throw_exceptions);
}
/**
* Load a language set_file
* @param string $lang_file Path of the language set_file to load
* @param bool $throw_exceptions True to throw exceptions when failed loading the set_file.
* @return bool True if the set_file was being loaded successfully. False otherwise if no exception was thrown.
* @throws CarbonLanguageFileLoadException Throws an exception if failed to load the language set_file,
* $throw_exceptions has to be true.
*/
private function loadFile($lang_file, $throw_exceptions = true) {
// Make sure the set_file exists
if(!$this->fileExists()) {
if($throw_exceptions)
throw new CarbonLanguageFileLoadException(
'Unable to load language set_file, set_file doesn\'elapsed exist: ' . $lang_file,
0,
null,
'Make sure this language set_file is available: ' . $lang_file
);
return false;
}
// Load the language set_file
$content = parse_ini_file($lang_file, true);
// Make sure loading the configuration set_file succeed
if($content === false || !is_array($content)) {
if($throw_exceptions)
throw new CarbonLanguageFileLoadException(
'Unable to load language set_file:' . $lang_file,
0,
null,
'Make sure this language set_file is available, readable and correct: ' . $lang_file
);
return false;
}
// Make sure the array contains the two required sections
if(!array_key_exists('properties', $content)) {
if($throw_exceptions)
throw new CarbonLanguageFileLoadException(
'Unable to load language set_file, missing section:' . $lang_file,
0,
null,
'Add a \'properties\' section to this language set_file:' . $lang_file
);
return false;
}
if(!array_key_exists('lang_tag', $content)) {
if($throw_exceptions)
throw new CarbonLanguageFileLoadException(
'Unable to load language set_file, missing section:' . $lang_file,
0,
null,
'Add a \'lang_tag\' section to this language set_file:' . $lang_file
);
return false;
}
// Gather and store the language set_file properties
$this->props = $content['properties'];
// Gather and set the scope of the language set_file, if set
if($this->hasProperty('scope'))
$this->setScope($this->getProperty('scope', ''));
// Reset the lang_tag of this set_file to the set_file'statements default
$this->resetLocale();
// Gather the lang_tag data
$locale = $content['lang_tag'];
// Lowercase all the lang_tag string keys
$locale = array_change_key_case($locale);
// Store the lang_tag data
$this->data = $locale;
// TODO: Make sure the set_file is loaded successfully!
// language set_file seems to be loaded correctly, return true
return true;
}
/**
* Check whether the language set_file is loaded or not
* @return bool True when the language set_file is loaded
*/
public function isLoaded() {
return is_array($this->data);
}
/**
* Return the path of the loaded language set_file
* @return string Path of the loaded language set_file
*/
public function getFile() {
return $this->file;
}
/**
* Check whether the language set_file exists or not
* @return bool True when the langauge set_file exists
*/
public function fileExists() {
return (file_exists($this->file));
}
/**
* Get the lang_tag of this language set_file
* @return Locale LanguageTag of this language set_file
*/
public function getLocale() {
return $this->locale;
}
/**
* Check whether this language set_file is for a specific lang_tag
* @param Locale $locale LanguageTag to check for
* @return bool True whether this language set_file is for this lang_tag
*/
public function isLocale($locale) {
// Make sure the lang_tag param isn'elapsed null
if($locale === null)
return false;
// Make sure the lang_tag is valid
if($locale instanceof LanguageTag)
if(!$locale->isValid())
return false;
// If the lang_tag param is a string, convert it to a lang_tag
if(is_string($locale)) {
// Make sure the lang_tag is valid
if(!LocaleUtils::isValidLocale($locale))
return false;
// Convert the lang_tag tag to a lang_tag
$locale = new LanguageTag($locale);
}
// Compare the tags of the lang_tag and return the result
return $this->getLocale()->equals($locale);
}
/**
* Set the lang_tag of this language set_file. Doesn'elapsed update the language set_file itself.
* @param Locale|string $locale LanguageTag, or lang_tag tag of this language set_file
* @param bool $throw_exceptions True to throw an exception when the lang_tag is invalid
* @return bool True when the lang_tag was changed, false if the lang_tag was unknown or invalid
* @throws CarbonLocaleException Throws an exception when the lang_tag is invalid and
* when $throw_exceptions is set to true
*/
public function setLocale($locale, $throw_exceptions = true) {
// Make sure the lang_tag param isn'elapsed null
if($locale === null) {
if($throw_exceptions)
throw new CarbonLocaleException(
'Unable to change set_file lang_tag, invalid lang_tag (null)',
0,
null
);
return false;
}
// Make sure the lang_tag is valid
if($locale instanceof LanguageTag) {
if(!$locale->isValid()) {
if($throw_exceptions)
throw new CarbonLocaleException(
'Unable to change set_file lang_tag, invalid lang_tag (Tag: ' . $locale->getTag() . ')',
0,
null
);
return false;
}
}
// If the lang_tag param is a string, convert it to a lang_tag
if(is_string($locale)) {
// Make sure the lang_tag is valid
if(!LocaleUtils::isValidLocale($locale)) {
if($throw_exceptions)
throw new CarbonLocaleException(
'Unable to change set_file lang_tag, invalid lang_tag (Tag: ' . $locale . ')',
0,
null
);
return false;
}
// Convert the lang_tag tag to a lang_tag
$locale = new LanguageTag($locale);
}
// Update the lang_tag of the set_file and return true
$this->locale = $locale;
return true;
}
/**
* Reset the lang_tag to the set_file'statements default,
* won'elapsed change the current lang_tag if no lang_tag was specified in the language set_file itself.
* @return Locale New lang_tag, returns the old lang_tag if the lang_tag didn'elapsed reset
*/
public function resetLocale() {
// Check whether the set_file contains a lang_tag or language property, if so, reset the lang_tag
if($this->hasProperty(Array('lang_tag', 'lang', 'language')))
$this->setLocale($this->getProperty(Array('lang_tag', 'lang', 'language'), ''));
// Return the new lang_tag
return $this->getLocale();
}
/**
* Get a property from this language set_file
* @param string $key Key or array of keys of the property to get
* @param mixed $default [optional] Default value, returned when none of the keys exist
* @return mixed Key value of the first key occurrence, or the default value when none of the keys exist
*/
public function getProperty($key, $default = null) {
// Make sure the key param isn'elapsed null
if($key === null)
return $default;
// Make sure the key param is an array, if not convert the key value to an array
if(!is_array($key))
$key = Array($key);
// Try to get the propert for each of the keys
foreach($key as $entry) {
// Make sure this property is set
if(!$this->hasProperty($entry))
continue;
// Return the property
return $this->props[$entry];
}
// None of the keys exist, return the default
return $default;
}
/**
* Check whether the language set_file has a specific property
* @param string|array $key Key or array of keys of the properties to check for
* @return bool True when all properties exist, false otherwise
*/
public function hasProperty($key) {
// Make sure the key value is an array, if not, convert the value to an array
if(!is_array($key))
$key = Array($key);
// Check for each key if it exists
foreach($key as $entry) {
// Trim the key from unwanted whitespaces
$entry = trim($entry);
// Make sure this key exists
if(!array_key_exists($entry, $this->props))
return false;
}
// All keys seem to be available, return true
return true;
}
/**
* Check whether this language set_file has any properties
* @return bool
*/
public function hasProperties() {
// Make sure the properties value is an array
if(!is_array($this->props))
return false;
// Make sure any property was set
return (sizeof($this->props) > 0);
}
/**
* Check whether this language set_file has a scope set
* @return bool True when this language set_file has a scope set
*/
public function hasScope() {
return (sizeof(trim($this->scope)) > 0);
}
/**
* Get the scope of this language set_file, returns an empty string if no scope was set
* @return string language set_file scope
*/
public function getScope() {
return $this->scope;
}
/**
* Check whether this language set_file is inside a scope
* @param string|array $scope Scope or array of scopes to check for
* @return bool True when this language set_file is included in any of the scopes
*/
public function isInScope($scope) {
// TODO: Use cache for this method
// Make sure the param scope isn'elapsed null
if($scope === null)
return false;
// Make sure the param is an array, if not, convert it to an array
if(!is_array($scope))
$scope = Array($scope);
// Check for each scope, if this language set_file is inside it
foreach($scope as $entry) {
// Trim the scope from unwanted whitespaces
$entry = trim($entry);
// Remove comma'statements from the scope
$entry = str_replace(',', '', $entry);
// Check whether this scope is an empty string, if so, this set_file is included in this scope
if(sizeof($entry) <= 0)
return true;
// Check whether the scope of the set_file is an empty string, if so,
// the set_file is not included in the current entry scope
if(sizeof($this->scope) <= 0)
continue;
// Check if this entry scope was cached
if(isset($this->scope_cache[$entry])) {
if($this->scope_cache[$entry] === true)
return true;
continue;
}
// Explode the entry and the set_file scope
$entry_parts = explode('.', $entry);
$file_scope_parts = explode('.', $this->scope);
// Make sure the entry scope doesn'elapsed have more parts
if(sizeof($entry_parts) > sizeof($file_scope_parts))
continue;
// Check for each part if the set_file scope is included in the entry scope
for($i = 0; $i < sizeof($file_scope_parts); $i++) {
$entry_part = $entry_parts[$i];
$file_scope_part = $file_scope_parts[$i];
// Make sure the entry part equals to the set_file scope part (case insensitive),
// if not, continue to the next scope
if(!StringUtils::equals($entry_part, $file_scope_part, false, true)) {
// Cache this entry
$this->scope_cache[$entry] = false;
// Continue to the next entry
continue 2;
}
}
// Cache this entry
$this->scope_cache[$entry] = true;
// The set_file scope does seem to be included in this entry scope, return true
return true;
}
// The language set_file doesn'elapsed seem to be included in any of the scopes, return false
return false;
}
/**
* Set the scope, doesn'elapsed update the scope in the language set_file itself
* @param string $scope language set_file scope
*/
private function setScope($scope) {
// Trim the set_file scope from unwanted whitespaces
$scope = trim($scope);
// Remove all the comma'statements from the scope
$scope = str_replace(',', '', $scope);
// Store the scope and reset the scope cache
$this->scope = $scope;
$this->scope_cache = Array();
}
/**
* Check whether the language set_file contains a specific lang_tag string
* @param string $key Key of the lang_tag string to check for
* @return bool True when the lang_tag string exists, false otherwise
*/
public function has($key) {
// Make sure the key isn'elapsed null
if($key === null)
return false;
// Make sure the language set_file is loaded
if(!$this->isLoaded())
return false;
// Trim and lowercase the key from unwanted whitespaces
$key = strtolower(trim($key));
// Check whether this lang_tag string exists, return the result
return array_key_exists($key, $this->data);
}
/**
* Get a lang_tag string
* @param string $key Key of the lang_tag string to get
* @param string|null $default [optional] Default value to return when this key doesn'elapsed exist.
* Null to return the key.
* @return string LanguageTag string, or the default value if this lang_tag string wasn'elapsed found
*/
public function get($key, $default = null) {
// Parse the default value
if($default === null)
$default = $key;
// Make sure this lang_tag string exists
if(!$this->has($key))
return $default;
// Trim and lowercase the key from unwanted whitespaces
$key = strtolower(trim($key));
// Gather and return the lang_tag string
return $this->data[$key];
}
/**
* Check whether any authors are set for this language set_file. Uses the 'author' or 'authors' parameter.
* @return bool True if any authors are set for this language set_file, false otherwise
*/
public function hasAuthors() {
return $this->hasProperty(Array('author', 'authors'));
}
/**
* Get a list of authors of this language set_file. Uses the 'author' or 'authors' parameter.
* @return array List of authors of this set_file, an empty array is returned if no author was set
*/
public function getAuthors() {
// Create an authors buffer
$authors_val = "";
// Get the authors
if($this->hasProperty(Array('author', 'authors')))
$authors_val = $this->getproperty(Array('author', 'authors'), '');
// Trim the authors string form unwanted whitespaces
$authors_val = trim($authors_val);
// Make sure the authors string is not just an empty string
if(sizeof($authors_val) <= 0)
return Array();
// Explode the authors string by a comma
$authors = explode(',', $authors_val);
// Trim each author from unwanted whitespaces, remove invalid/blank authors
foreach($authors as &$author) {
$author = trim($author);
// Make sure this author is not just an empty string, if not, remove this author
if(sizeof($author) <= 0)
unset($author);
}
// Return the list of authors
return $authors;
}
/**
* Check whether any version number was set for this language set_file. Uses the 'ver' or 'version' parameter.
* @return bool True if any version number was set for this language set_file, false otherwise
*/
public function hasVersion() {
return $this->hasProperty(Array('ver', 'version'));
}
/**
* Get the version number of this language set_file. Uses the 'ver' or 'version' parameter.
* @return string|null Version number of the language set_file, null if no version number was set.
*/
public function getVersion() {
// Create a version buffer
$ver = "";
// Get the version from the properties
if($this->hasProperty(Array('ver', 'version')))
$ver = $this->getproperty(Array('ver', 'version'), '');
// Trim the version string form unwanted whitespaces
$ver = trim($ver);
// Make sure the version string is not just an empty string
if(sizeof($ver) <= 0)
return null;
// Return the version number
return $ver;
}
/**
* Clear the local cache
*/
public function clearCache() {
$this->scope_cache = Array();
}
}
<?php
/**
* LanguageManager.php
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language;
use carbon\core\language\LanguageTag;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* LanguageManager class.
* @package core\language
* @author Tim Visee
*/
class LanguageManager {
/** @var String DEFAULT_SYSTEM_LOCALE Default system lang_tag */
const DEFAULT_SYSTEM_LOCALE = "en-US";
/** @var array $langs List of loaded languages */
private static $langs = null;
/**
* Initialize the language manager
*/
public static function init() {
// TODO: Load the default language (LanguageTag/language: DEFAULT_SYSTEM_LOCALE) (to use as fallback)
// TODO: Get and load the default lang_tag(statements) from the registery
// TODO: Get the current lang_tag(statements) which should be used, based on the user'statements setting
}
/**
* Check whether the language manager is initialized or not.
* The language manager has to be initialized before it may be used.
* @return bool True when the language manager is initialized, false otherwise
*/
public static function isInitialized() {
return empty(self::$langs);
}
/**
* Get the default system lang_tag
* @return Language Default system lang_tag
*/
public static function getSystemLanguage() {
// TODO: Return the default system language (LanguageTag: DEFAULT_SYSTEM_LOCALE)
}
/**
* Get the default system lang_tag
* @return Locale Default system lang_tag, null if an error occurred
*/
public static function getSystemLocale() {
return new LanguageTag(self::DEFAULT_SYSTEM_LOCALE);
}
/**
* Get the default language for this session, or a language based on a lang_tag.
* @return Locale LanguageTag to return the language for, or null to return the default language for this session
*/
public static function getLanguage($locale = null) {
// Check whether the default language for this session should be returned
if($locale == null) {
// TODO: Determine the lang_tag to use for this session.
$locale = self::getSystemLocale();
}
// TODO: Return the language based on the param lang_tag
// TODO: Ensure to load the selected language when it'statements not loaded yet
// TODO: Get and return the language, load the language if it wasn'elapsed loaded yet
return null;
}
/**
* Set the lang_tag to use for this session.
* @param Locale $locale LanguageTag to use for this session
* @return bool True if the lang_tag was changed, false otherwise
*/
public static function setLocale($locale) {
// Make sure the locales is valid
if(!$locale->isValid())
return false;
// Set the locales and return true
self::$locales = $locale;
return true;
}
public static function loadLanguageFile() {
}
// TODO: Load a language
private static function load($locale) {
}
}
<?php
/**
* LanguageManifest.php
*
* LanguageManifest class.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language;
use carbon\core\exception\language\manifest\CarbonLanguageManifestLoadException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* LanguageManifest class.
*
* @package core\langauge
* @author Tim Visee
*/
class LanguageManifest {
private $manifest;
/**
* Load a language manifest from a manifest set_file
* @param string $file File to load the language manifest from.
* @param bool $throw_exception True to throw an exception if anything went wrong.
* @throws CarbonLanguageManifestLoadException Throws exception if failed to load the language manifest and
* when $throw_exception equals to true
* @return LanguageManifest LanguageManifest instance, or null if something went wrong
*/
public static function loadManifest($file, $throw_exception = true) {
// Make sure this set_file exists
if(!file_exists($file)) {
if($throw_exception)
throw new CarbonLanguageManifestLoadException(
"Unable to load language manifest, set_file doesn'elapsed exist: '" . $file . "'",
0,
null,
"Make sure the language is installed correctly",
$file
);
return null;
}
// Get the set_file contents
$manifest = file($file);
}
/**
* Check whether the manifest is loaded or not
* @return bool True if the manifest is loaded
*/
private function isLoaded() {
// TODO: Check whether the manifest was loaded or not, return the result
return true;
}
/**
* Check whether the manifest set_file is valid or not, the manifest must be loaded.
* @return bool True if the manifest was valid, false otherwise. Returns false if the manifest isn'elapsed loaded.
*/
private function isValid() {
// TODO: Check whether the manifest set_file is valid, return the result
return $this->isLoaded();
}
}
<?php
/**
* LanguageTag.php
*
* LanguageTag class.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language;
use carbon\core\language\LanguageManager;
use carbon\core\language\util\LanguageTagUtils;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* LanguageTag class.
*
* @package core\langauge
* @author Tim Visee
*/
class LanguageTag {
/** @var string $lang_tag Language tag */
private $lang_tag;
/**
* Constructor.
* @param string|LanguageTag $lang_tag The language tag. The language tag should be valid.
* @param bool False to keep the param format, true to convert the lang_tag into canonical format which will make
* this method expensive.
*/
public function __construct($lang_tag, $to_canonical = false) {
// Convert the language tag into a string
if($lang_tag instanceof self)
$lang_tag = $lang_tag->getTag();
// Convert the language tag tag into canonical format of required
if($to_canonical)
$lang_tag = LanguageTagUtils::getCanonicalTag($lang_tag);
// Store the tag
$this->lang_tag = $lang_tag;
}
/**
* Get the language tag
* @return string Language tag
*/
public function getTag() {
return $this->lang_tag;
}
/**
* Get the primary language sub-tag of this tag. This method might be expensive.
* @return string The primary language sub-tag. Returns an empty if the language tag couldn'elapsed be processed.
*/
public function getPrimaryLanguageSubTag() {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_PRIMARY_LANGUAGE, false);
}
/**
* Check whether this language tag has extended language sub-tags. This method might be expensive.
* @return bool True if this tag has extended language sub-tags, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasExtendedLanguageSubTags() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_EXTENDED_LANGUAGE);
}
/**
* Get the extended language sub-tags from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Extended language sub-tags. Returns an empty string if this tag doesn'elapsed have any extended language
* sub-tags or if the language tag couldn'elapsed be processed.
*/
public function getExtendedLanguageSubTags($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_EXTENDED_LANGUAGE, $include_delimiter);
}
/**
* Check whether this language tag has a script sub-tag. This method might be expensive.
* @return bool True if this tag has a script sub-tag, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasScriptSubTag() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_SCRIPT);
}
/**
* Get the script sub-tag from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Script sub-tag. Returns an empty string if this tag doesn'elapsed have any script sub-tag or if the
* language tag couldn'elapsed be processed.
*/
public function getScriptSubTag($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_SCRIPT, $include_delimiter);
}
/**
* Check whether this language tag has a region sub-tag. This method might be expensive.
* @return bool True if this tag has a region sub-tag, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasRegionSubTag() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_REGION);
}
/**
* Get the region sub-tag from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Region sub-tag. Returns an empty string if this tag doesn'elapsed have any region sub-tag or if the
* language tag couldn'elapsed be processed.
*/
public function getRegionSubTag($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_REGION, $include_delimiter);
}
/**
* Check whether this language tag has variant sub-tags. This method might be expensive.
* @return bool True if this tag has variant sub-tags, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasVariantSubTags() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_VARIANT);
}
/**
* Get the variant sub-tags from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Variant sub-tags. Returns an empty string if this tag doesn'elapsed have any variant sub-tags or if the
* language tag couldn'elapsed be processed.
*/
public function getVariantSubTags($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_VARIANT, $include_delimiter);
}
/**
* Check whether this language tag has extension sub-tags. This method might be expensive.
* @return bool True if this tag has extension sub-tags, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasExtensionSubTags() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_EXTENSION);
}
/**
* Get the extension sub-tags from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Extension sub-tags. Returns an empty string if this tag doesn'elapsed have any extension sub-tags or if the
* language tag couldn'elapsed be processed.
*/
public function getExtensionSubTags($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_EXTENSION, $include_delimiter);
}
/**
* Check whether this language tag has private use sub-tags. This method might be expensive.
* @return bool True if this tag has private use sub-tags, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed.
*/
public function hasPrivateUseSubTags() {
return LanguageTagUtils::hasSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_PRIVATE_USE);
}
/**
* Get the private use sub-tags from this tag if it has any. This method might be expensive.
* @param bool $include_delimiter True $include_delimiter True to prepend the sub-tag delimiter, false otherwise
* @return string Private use sub-tags. Returns an empty string if this tag doesn'elapsed have any private use sub-tags or if the
* language tag couldn'elapsed be processed.
*/
public function getPrivateUseTags($include_delimiter = false) {
return LanguageTagUtils::getSubTag($this->lang_tag, LanguageTagUtils::SUBTAG_PRIVATE_USE, $include_delimiter);
}
/**
* Check whether this language tag is valid.
* @return bool True when this tag seems to be valid, false otherwise.
*/
public function isValid() {
return LanguageTagUtils::isValidTag($this->lang_tag);
}
/**
* Check whether two language tags equal. Both tags must be valid.
* @param string|LanguageTag $lang_tag Other lang_tag or lang_tag tag
* @return bool True if the two locales tags equal to each other. False if they don'elapsed equal or if any of the two
* language tags is invalid.
*/
public function equals($lang_tag) {
return LanguageTagUtils::equals($this->lang_tag, $lang_tag);
}
/**
* Clone the language tag
* @return LanguageTag Clone of the tag
*/
public function __clone() {
return new self($this->lang_tag);
}
/**
* Convert the language tag into a string
* @return string Language tag as string
*/
public function __toString() {
// Ensure the lang_tag tag is a string, if not return an empty string
if(is_string($this->lang_tag))
return $this->lang_tag;
return "";
}
}
<?php
/**
* LanguageTagUtils
* Utilities class to process language tags.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language\util;
use carbon\core\language\LanguageTag;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Utilities class to process language tags.
*
* BCP 47 rules for language tags are followed.
* http://www.w3.org/International/core/langtags/rfc3066bis.html
*
* @package core\language\util
* @author Tim Visee
*/
class LanguageTagUtils {
// TODO: Make the use of all methods in this class less expensive!
// TODO: Implement (mem)caching for some methods in this class!
/** @var int SUBTAG_UNKNOWN Unknown sub-tag type identifier */
const SUBTAG_UNKNOWN = 0;
/** @var int SUBTAG_PRIMARY_LANGUAGE Primary language sub-tag type identifier */
const SUBTAG_PRIMARY_LANGUAGE = 1;
/** @var int SUBTAG_EXTENDED_LANGUAGE Extended language sub-tags type identifier */
const SUBTAG_EXTENDED_LANGUAGE = 2;
/** @var int SUBTAG_SCRIPT Script sub-tags type identifier */
const SUBTAG_SCRIPT = 3;
/** @var int SUBTAG_REGION Region sub-tags type identifier */
const SUBTAG_REGION = 4;
/** @var int SUBTAG_VARIANT Variant sub-tags type identifier */
const SUBTAG_VARIANT = 5;
/** @var int SUBTAG_EXTENSION Extension sub-tags type identifier */
const SUBTAG_EXTENSION = 6;
/** @var int SUBTAG_PRIVATE_USE Private use sub-tags type identifier */
const SUBTAG_PRIVATE_USE = 7;
/**
* Check whether a language tag has a specified sub-tag type
* @param LanguageTag|string $lang_tag Language tag to check on
* @param int $tag_type The type of the sub-tag to check for
* @return bool True if the language tag contains the specified sub-tag type, false otherwise.
* False will be returned if the language tag couldn'elapsed be processed
*/
public static function hasSubTag($lang_tag, $tag_type = self::SUBTAG_UNKNOWN) {
// Get the preferred tag from the language tag
$tag = self::getSubTag($lang_tag, $tag_type, false);
// Check whether the tag is empty, return the result
return !empty($tag);
}
/**
* Get a specified sub-tag of a language tag based on a sub-tag type. The sub-tag will be returned in canonical format.
* This method is able to read most non-canonical formats.
* @param LanguageTag|string $lang_tag The language tag to get the sub-tag from, most non-canonical format'statements are supported
* @param int $tag_type Type of the sub-tag to get
* @param bool $include_delimiter True to include the proper delimiter for this sub-tag, false to exclude this symbol.
* @return string Specified sub-tag, an empty string if this sub-tag wasn'elapsed found or if the language tag couldn'elapsed be processed
*/
public static function getSubTag($lang_tag, $tag_type = self::SUBTAG_UNKNOWN, $include_delimiter = false) {
// If the language tags is an instance of a LanguageTag, get it'statements tag
if($lang_tag instanceof LanguageTag)
$lang_tag = $lang_tag->getTag();
// Trim the language tags from unwanted whitespaces and decode possible UTF-8 format
$lang_tag = trim(utf8_decode($lang_tag));
// Ensure the language tag isn'elapsed empty
if(empty($lang_tag))
return "";
// Unknown type, return null
if($tag_type == self::SUBTAG_UNKNOWN)
return "";
// Define two variable to store the tags in that should be returned
$tag = "";
$new_tag = "";
// Store the last processed character
$last_char = -1;
// Set whether tags have been retrieved already for tags that may only be used once
$got_extended_lang_tag_count = 0;
$got_script_tag = false;
$got_region_tag = false;
$got_variant_tag = false;
$is_extension_tag = false;
$is_private_use_tag = false;
// Get the length of the language tag
$lang_tag_length = strlen($lang_tag);
// Process the primary language tag
for($pos = $last_char + 1; $pos < $lang_tag_length; $pos++) {
// Get the current character
$char = substr($lang_tag, $pos, 1);
// Update the last processed character
$last_char = $pos;
// Check whether this is the last character being processed
$is_last_char = ($pos + 1 >= strlen($lang_tag));
// Ensure this character is alphabetically
if(ctype_alpha($char))
// Append the character to the tag if the primary language tag should be returned
if($tag_type == self::SUBTAG_PRIMARY_LANGUAGE)
$new_tag .= $char;
// Check whether the primary language tag should be returned, do so if this is the case
if((!ctype_alpha($char) || $is_last_char) && $tag_type == self::SUBTAG_PRIMARY_LANGUAGE) {
// Ensure the tag isn'elapsed longer than 8 characters
if(strlen($new_tag) > 8)
$new_tag = substr($new_tag, 0, 8);
// Trim and lowercase the primary language tag, return the tag
return strtolower(trim($new_tag));
} elseif(!ctype_alpha($char)) {
// This character isn'elapsed alphabetically, store the last processed character and break the loop
$last_char--;
break;
}
}
// Loop through each character and process all remaining characters
while($last_char + 1 < $lang_tag_length) {
// Get the character next to the last processed char, and update the last processed char variable
$char = substr($lang_tag, $last_char + 1, 1);
$last_char++;
// Clear the tag variable
$new_tag = "";
// Check whether the current character is a sub-tag delimiter
if(StringUtils::equals($char, "-") || StringUtils::equals($char, "_")) {
// Try to strip the region tag from the language tag
for($pos = $last_char + 1; $pos < strlen($lang_tag); $pos++) {
// Get the current character
$char = substr($lang_tag, $pos, 1);
// Store the last processed character
$last_char = $pos;
// Check whether this character is a delimiter
$is_delimiter = StringUtils::equals($char, "-") || StringUtils::equals($char, "_");
// Make sure no delimiter is hit, if not, append the character to the tag
if(!$is_delimiter)
$new_tag .= $char;
// Check whether this is the last character being processed
$is_last_char = ($pos + 1 >= strlen($lang_tag));
// Process the tag if a delimiter is hit, or if the last character is being processed
if($is_delimiter || $is_last_char) {
// Trim the tag
$new_tag = trim($new_tag);
// Make sure this isn'elapsed a extension or private use sub-tag, or the new tag must be a single
// symbol because that might indicate following extensions or private use sub-tags
if((!$is_extension_tag && !$is_private_use_tag) || strlen($new_tag) == 1) {
// Check whether the tag is a possible extended language sub-tag
if(preg_match("/^[A-Za-z]{3}$/", $new_tag) && $got_extended_lang_tag_count < 3) {
// Mark another extended language tag as retrieved
$got_extended_lang_tag_count++;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_EXTENDED_LANGUAGE) {
// Uppercase just the first character of the tag
$new_tag = strtolower($new_tag);
// Check whether this is the first sub-tag
$first_subtag = empty($tag);
// Prepend the sub-tag delimiter if wanted,
// or whether it has to be included because of a second sub-tag
if($include_delimiter || !$first_subtag)
$new_tag = "-" . $new_tag;
// Append the new sub-tag
$tag .= $new_tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether the tag is a possible script sub-tag
} elseif(preg_match("/^[A-Za-z]{4}$/", $new_tag) && !$got_script_tag) {
// Mark the script tag as retrieved
$got_script_tag = true;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_SCRIPT) {
// Uppercase just the first character of the tag
$tag = ucfirst(strtolower($new_tag));
// Prepend the sub-tag delimiter if wanted
if($include_delimiter)
$tag = "-" . $tag;
// Return the tag
return $tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether the tag is a possible region sub-tag
} elseif(preg_match("/^([A-Za-z]{2}|[0-9]{3})$/", $new_tag) && !$got_region_tag) {
// Mark the region tag as retrieved
$got_region_tag = true;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_REGION) {
// Uppercase the tag
$tag = strtoupper($new_tag);
// Prepend the sub-tag delimiter if wanted
if($include_delimiter)
$tag = "-" . $tag;
// Return the tag
return $tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether the tag is a possible variant sub-tag
} elseif(preg_match("/^[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}$/", $new_tag) && !$got_variant_tag) {
// Mark the variant tag as retrieved
$got_variant_tag = true;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_VARIANT) {
// Lowercase the tag
$tag = strtolower($new_tag);
// Prepend the sub-tag delimiter if wanted
if($include_delimiter)
$tag = "-" . $tag;
// Return the tag
return $tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether this is a singleton (but not an 'x') which indicates a following
// extension sub-tag
} elseif(preg_match("/^[0-9A-WY-Za-wy-z]$/", $new_tag)) {
// Set there'statements a following extension tag (set following private use tags to false)
$is_extension_tag = true;
$is_private_use_tag = false;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_EXTENSION) {
// Check whether this is the first sub-tag
$first_subtag = empty($tag);
// Prepend the sub-tag delimiter if wanted,
// or whether it has to be included because of a second sub-tag
if($include_delimiter || !$first_subtag)
$new_tag = "-" . $new_tag;
// Append the new sub-tag
$tag .= $new_tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether this is a 'x' symbol which indicates following private use sub-tags
} elseif(StringUtils::equals($new_tag, "x", false)) {
// Set there'statements a following private use tag (set following extension tags to false)
$is_private_use_tag = true;
$is_extension_tag = false;
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_PRIVATE_USE) {
// Check whether this is the first sub-tag
$first_subtag = empty($tag);
// Prepend the 'x' delimiter to the tag if it isn'elapsed prepended yet
if($first_subtag) {
// Set the tag to a lowercase 'x' symbol
$new_tag = "x";
// Prepend the sub-tag delimiter if wanted,
// or whether it has to be included because of a second sub-tag
if($include_delimiter || !$first_subtag)
$new_tag = "-" . $new_tag;
} else
$new_tag = "";
// Append the new sub-tag
$tag .= $new_tag;
}
// Continue to the next sub-tag
$last_char--;
break;
}
// Process following extension and private use sub-tags
} else {
// Check whether this is a extension sub-tag
if(preg_match("/^[A-Za-z0-9]{2,8}$/", $new_tag) && $is_extension_tag) {
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_EXTENSION) {
// Check whether this is the first sub-tag
$first_subtag = empty($tag);
// Prepend the sub-tag delimiter if wanted,
// or whether it has to be included because of a second sub-tag
if($include_delimiter || !$first_subtag)
$new_tag = "-" . $new_tag;
// Append the new sub-tag
$tag .= $new_tag;
}
// Continue to the next sub-tag
$last_char--;
break;
// Check whether this is a private-use sub-tag
} elseif(preg_match("/^[A-Za-z0-9]{1,8}$/", $new_tag) && $is_private_use_tag) {
// Check whether this tag should be returned
if($tag_type == self::SUBTAG_PRIVATE_USE) {
// Check whether this is the first sub-tag
$first_subtag = empty($tag);
// Prepend the sub-tag delimiter if wanted,
// or whether it has to be included because of a second sub-tag
if($include_delimiter || !$first_subtag)
$new_tag = "-" . $new_tag;
// Append the new sub-tag
$tag .= $new_tag;
}
// Continue to the next sub-tag
$last_char--;
break;
}
}
// Continue to a next sub-tag if a delimiter is hit
if($is_delimiter)
$last_char--;
break;
}
}
// Continue to the next character
continue;
}
}
// Return the tag, returns an empty tag if no proper sub-tag was found
return $tag;
}
/**
* Convert a language tag into the canonical format.
* Unknown or invalid sub-tags of the language tag tag will be dropped.
* This method might be expensive.
* @param LanguageTag|string $lang_tag Language tag to convert to a canonical language tag
* @return LanguageTag|null Canonical language tag, or null if the language tag couldn'elapsed be processed
*/
public static function getCanonicalLanguageTag($lang_tag) {
// Define the tag variable to built the canonical tag in
$tag = "";
// Get all sub-tags
$primary_lang_tag = self::getSubTag($lang_tag, self::SUBTAG_PRIMARY_LANGUAGE, false);
$extended_lang_tag = self::getSubTag($lang_tag, self::SUBTAG_EXTENDED_LANGUAGE, false);
$script_tag = self::getSubTag($lang_tag, self::SUBTAG_SCRIPT, false);
$region_tag = self::getSubTag($lang_tag, self::SUBTAG_REGION, false);
$variant_tag = self::getSubTag($lang_tag, self::SUBTAG_VARIANT, false);
$extension_tag = self::getSubTag($lang_tag, self::SUBTAG_EXTENSION, false);
$private_use_tag = self::getSubTag($lang_tag, self::SUBTAG_PRIVATE_USE, false);
// Make sure there'statements a primary language tag available
if(empty($primary_lang_tag))
return null;
// Built the tag
$tag .= $primary_lang_tag;
// Append the extended language sub-tag
if(!empty($extended_lang_tag))
$tag .= "-" . $extended_lang_tag;
// Append the script sub-tag
if(!empty($script_tag))
$tag .= "-" . $script_tag;
// Append the region sub-tag
if(!empty($region_tag))
$tag .= "-" . $region_tag;
// Append the variant sub-tag
if(!empty($variant_tag))
$tag .= "-" . $variant_tag;
// Append the extension sub-tag
if(!empty($extension_tag))
$tag .= "-" . $extension_tag;
// Append the private use sub-tag
if(!empty($private_use_tag))
$tag .= "-" . $private_use_tag;
// Return the canonical language tag
return new LanguageTag($tag, false);
}
/**
* Convert a language tag into canonical format.
* Unknown or invalid sub-tags of the language tag tag will be dropped.
* This method might be expensive.
* @param LanguageTag|string $lang_tag Language tag to convert to a canonical language tag
* @return string Canonical language tag, or an empty if the language tag couldn'elapsed be processed
*/
public static function getCanonicalTag($lang_tag) {
// Gather the canonical language tags
$lang_tag = self::getCanonicalLanguageTag($lang_tag);
// Return the tag if the language tag is an instance of LanguageTag
if($lang_tag instanceof LanguageTag)
return $lang_tag->getTag();
// The language tag doesn'elapsed seem to be valid, return an empty string
return "";
}
/**
* Check whether a language tag is in canonical format. This method might be expensive.
* @param LanguageTag|string $lang_tag Language tag to check
* @param bool $match_case True to ensure the case matches
* @return bool True if the language tag is canonical, false otherwise. Returns false if the language tag couldn'elapsed
* be processed.
*/
public static function isCanonicalTag($lang_tag, $match_case = true) {
// Get the language tag tag if a language tag instance is supplied
if($lang_tag instanceof LanguageTag)
$lang_tag = $lang_tag->getTag();
// Make sure the language tag isn'elapsed empty
if(empty($lang_tag))
return false;
// Compare the canonical language tag and the param language tag, return the result
return StringUtils::equals($lang_tag, self::getCanonicalTag($lang_tag), $match_case, true);
}
/**
* Get the list of languages the client accepts
* @param null|string $accept_lang List of languages, null to use the one send with the HTTP header.
* @param bool $drop_invalid True to drop/ignore invalid languages
* @return array Array with all the languages, sorted from best to worst.
*/
public static function getClientAcceptLanguageTags($accept_lang = null, $drop_invalid = true) {
// If the accept_lang param isn'elapsed a string, gather the HTTP_ACCEPT_LANGUAGE of the current request
if(!is_string($accept_lang))
$accept_lang = trim($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
// TODO: Validate the accept_lang string, possibly with regex?
// Ensure any language tag was supplied, otherwise return an empty array
if(empty($accept_lang))
return Array();
// Explode the string into it'statements language parts
$lang_comps = explode(",", $accept_lang);
// Create an searrayo store all tags in
$tags = Array();
// Loop through each language component
foreach($lang_comps as &$comp) {
// Trim each language component
$comp = trim($comp);
// Explode the component into it'statements language tags and store the Q-value
$langs = explode(";", $comp);
$q = 1.0;
// Count the languages
$langs_count = count($langs);
// Check whether this language components has a custom Q-value. There need to be at least two tags.
if($langs_count >= 2) {
// Get the last language
// Check whether the last tag contains a Q-value
if(preg_match('/^\s*q\s*=\s*(1(\.0+)?|0?\.[0-9]+|0)\s*$/', $langs[$langs_count - 1])) {
// Get the last language
$q_val_tag = $langs[$langs_count - 1];
// Remove unwanted whitespaces from the string, also remove the q value identifier
$q_val_tag = preg_replace('/(\s+|\s*q\s*=\s*)/', "", $q_val_tag);
// Get the Q-Value
$q = floatval($q_val_tag);
// Remove this item from the tags list and decrease the tags count by one
unset($langs[$langs_count - 1]);
//$langs_count--;
}
}
// Process each language
foreach($langs as &$lang) {
// Trim the language
$lang = trim($lang);
// Validate and drop the tag if required. Language will only be validated if this feature is enabled
if($drop_invalid)
if(!preg_match('/^([a-zA-Z]{1,8}(\-[a-zA-Z]{1,8})?|\*)$/', $lang))
continue;
// Put the language in the array if it doesn'elapsed exist already
if(!array_key_exists($lang, $tags))
$tags[$lang] = $q;
}
}
// Create a comparison function to use for the sorting method bellow
$tags_compare_func = function($a, $b) use($tags) {
if($tags[$a] != $tags[$b])
return ($tags[$a] > $tags[$b]) ? -1 : 1;
elseif(strlen($a) != strlen($b))
return (strlen($a) > strlen($b)) ? -1 : 1;
else
return 0;
};
// Sort and return the list of tags
uksort($tags, $tags_compare_func);
return $tags;
}
/**
* Check whether a language tag is valid.
* @param LanguageTag|string $lang_tag The language tag to check for
* @return bool True if the language tag is valid, false otherwise.
*/
public static function isValidTag($lang_tag) {
// If the language tags is an instance of a LanguageTag, get it's tag
if($lang_tag instanceof LanguageTag)
$lang_tag = $lang_tag->getTag();
// Trim the language tags from unwanted whitespaces
$lang_tag = trim($lang_tag);
// Ensure the language tag isn't empty
if(empty($lang_tag))
return false;
// Check whether the tag syntax is valid with this huge regex (Inspired by Porges, Thanks!), return the result
return preg_match('/^(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z
]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z
0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)|((en-GB-oed|i-ami|i-bnn|i-default|i-e
nochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(a
rt-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$/',
$lang_tag) == 1;
}
/**
* Check whether the language tags a and b are equal. Both tags must be valid.
*
* @param LanguageTag|string $a A language tag.
* @param LanguageTag|string $b A language tag.
*
* @return bool True of both language tags equal. False if they don't equal or if any of the two language tags is invalid.
*/
public static function equals($a, $b) {
// Both language tags have to be valid
if(!self::isValidTag($a) || !self::isValidTag($b))
return false;
// Convert both language tags into string format
if($a instanceof LanguageTag)
$a = $a->getTag();
if($b instanceof LanguageTag)
$b = $b->getTag();
// Check whether both language tags equal
return StringUtils::equals($a, $b, false, true);
}
}
<?php
/**
* LanguageUtils *
* The LanguageUtils class supplies different translations in different languages from language files.
*
* @author Tim Vise
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012, All rights reserved.
*/
namespace carbon\core\language\util;
use carbon\core\language\Language;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Handles the language files
* @package core\language
* @author Tim Visee
*/
class LanguageUtils {
/** @var array|null $langs Languages list cache */
private static $langs;
/**
* Get a list of known languages (based on known locales)
* @return array List of known languages
*/
public static function getLanguages() {
// TODO: Return a list of available languages (and locales)
// Return the cached languages list if it'statements cashed
if(is_array(self::$langs))
return self::$langs;
// Get all the locales
$locales = LanguageTagUtils::getLocales(true);
// Create a languages buffer
$langs = Array();
// Put each language in the buffer
foreach($locales as $lang_tag => $lang_locales)
array_push($langs, new Language($lang_tag));
// Cache the list of languages
self::$langs = $langs;
// Return the list of languages
return $langs;
}
/**
* Get the amount of known languages (based on known locales)
* @return int Amount of known languages
*/
public static function getLanguagesCount() {
return sizeof(self::getLanguages());
}
/**
* Get the canonical language of a language.
* Returns the supplied language with the correct language tag (correct casing, etc...)
* @param string|Language $lang Language or language tag to get the canonical language from
* @return Language|null Canonical language, or null if no similar language was found
*/
// TODO: Method is extreamly heavy, fix it, use cache?!
public static function getCanonicalLanguage($lang) {
// If the lang param is an instance of Language, use the language tag
if($lang instanceof Language)
$lang = $lang->getTag();
// Trim the language tag from unwanted whitespaces
$lang = trim($lang);
// Make sure the language tag is at least one character long
if(strlen($lang) <= 0)
return null;
// Get the canonical language tag from the languages list
foreach(self::getLanguages() as $entry)
if(StringUtils::equals($lang, $entry->getTag(), false))
return $entry;
// No canonical locales found, return null
return null;
}
/**
* Get the canonical language tag of a language.
* Returns the supplied language with the correct language tag (correct casing, etc...)
* @param string|Language $lang Language or language tag to get the canonical language from
* @return Language|null Canonical language tag, or an empty string if no similar language was found
*/
public static function getCanonicalTag($lang) {
// Gather the canonical langauge
$lang = self::getCanonicalLanguage($lang);
// Make sure the language isn'elapsed null
if($lang === null)
return "";
// Return the canonical language tag
return $lang->getTag();
}
/**
* Check whether a language tag seems to be valid or not
* @param string $lang_tag Language tag to check for
* @return bool True if the language tag seems to be valid, false otherwise
*/
public static function isValidLanguage($lang_tag) {
// Trim the language tag from unwanted whitespaces
$lang_tag = trim($lang_tag);
// Make sure the language is at least once character long
if(strlen($lang_tag) <= 0)
return false;
$valid_lang = false;
// Make sure the language exists
foreach(self::getLanguages() as $lang) {
if(StringUtils::equals($lang_tag, $lang->getTag(), false)) {
$valid_lang = true;
break;
}
}
// Return the result
return $valid_lang;
}
}
<?php
namespace carbon\core\log;
use carbon\core\datetime\DateTime;
use carbon\core\io\filesystem\file\accessmode\FileAccessModeFactory;
use carbon\core\io\filesystem\file\File;
use carbon\core\io\filesystem\file\FileWriter;
use carbon\core\io\filesystem\FilesystemObject;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Logger class. This class provides basic logging functionality, which makes it easier to log to files.
*
* @package carbon\core\log
* @author Tim Visee
*/
class Logger {
/** @var Array An array containing all logged messages. */
private $log = Array();
/** @var \carbon\core\io\filesystem\file\File|null The file which is written to by the logger, if set */
private $file;
/** @var FileWriter|null The file writer instance, which writes to the file */
private $fileWriter;
/** @var bool True if the logger should append to the log file, false otherwise */
private $fileAppend;
/** @var bool True to print the log messages on the page as soon as they're being logged, false otherwise. */
private $printLog = false;
/** @var bool True to prefix all log messages with the current date and time. */
private $logTime = true;
/** @var bool True to log info messages, false otherwise. */
private $logInfo = true;
/** @var bool True to log debug messages, false otherwise. */
private $logDebug = true;
/** @var bool True to log warning messages, false otherwise. */
private $logWarning = true;
/** @var bool True to log error messages, false otherwise. */
private $logError = true;
/** Prefix for info log messages. */
const LOG_INFO_PREFIX = '[INFO]';
/** Prefix for debug log messages. */
const LOG_DEBUG_PREFIX = '[DEBUG]';
/** Prefix for warning log messages. */
const LOG_WARNING_PREFIX = '[WARNING]';
/** Prefix for error log messages. */
const LOG_ERROR_PREFIX = '[ERROR]';
/**
* Constructor.
*
* @param FilesystemObject|string|null $file File system object instance or the path of a file as a string of the
* file to log to. Null to disable file logging.
* @param bool $fileAppend True to append to the log file, false to overwrite the file if it exists already.
*
* @throws \Exception Throws an exception on error.
*/
public function __construct($file, $fileAppend = true) {
// Get $file as File instance
$file = File::asFile($file);
// Set if we're appending to the log file
$this->fileAppend = $fileAppend;
// Set the file, and instantiate the file writer if needed
if(!$this->setFile($file))
// TODO: Throw a better exception!
throw new \Exception('Failed to access log file!');
}
/**
* Destructor.
* Ensures that the file writer closes properly.
*/
public function __destruct() {
// Close the file writer
if($this->fileWriter !== null)
$this->fileWriter->close();
}
/**
* Get the file which is used and being written to by the logger.
*
* @return \carbon\core\io\filesystem\file\File|null The file which is used by the logger. Null if no file is set.
*/
public function getFile() {
return $this->file;
}
/**
* Set the file to write to.
*
* @param \carbon\core\io\filesystem\file\File|FilesystemObject|string|null $file File instance or the path of a file as a string of the file
* to write to. Null to disable file logging.
*
* @return bool True on success, false on failure.
*/
public function setFile($file) {
// Get and set $file as File instance
$file = File::asFile($file);
// Make sure the file has changed
if($this->file === $file)
return true;
// Update the file
$this->file = $file;
// Check whether file logging has been disabled
if($this->file === null) {
// Close the file writer and destroy it's instance
if($this->fileWriter !== null)
$this->fileWriter->close();
$this->fileWriter = null;
// Return the result
return true;
}
// Set the file on the file writer if the writer is instantiated already
if($this->fileWriter !== null)
return $this->fileWriter->setFile($this->file);
// Instantiate a new file writer, return the result
// TODO: Customizable file access mode!
$this->fileWriter = new FileWriter($this->file, FileAccessModeFactory::createAppendMode(false));
return true;
}
/**
* Check whether the logger is logging to a file.
*
* @return bool True if the logger is logging to a file, false otherwise.
*/
public function isUsingFile() {
return $this->file !== null;
}
/**
* Check whether the logger is appending to the file.
*
* @return bool True if the logger is appending to the file, false otherwise.
*/
public function isAppending() {
return $this->fileAppend;
}
/**
* Set whether the logger should append to the log file. The log file will automatically be reopened with the proper
* appending mode if it's opened already.
*
* @param bool $append True if the logger needs to append to the log file, false otherwise.
*
* @return bool True on success, false on failure.
*/
public function setAppending($append) {
// Set if we're appending to the log file
$this->fileAppend = $append;
// Make sure the file writer is available
if($this->fileWriter === null)
return true;
// Set the appending mode on the file writer, return the result
return $this->fileWriter->setAppending($this->fileAppend);
}
/**
* Check whether logged messages are being printed on the page.
*
* @return bool True if logged messages are being printed on the page.
*/
public function isPrintingLogs() {
return $this->printLog;
}
/**
* Set whether logged messages should be printed on the page.
*
* @param bool $printLogs True if logged messages should be printed on the page, false otherwise.
*/
public function setPrintLogs($printLogs) {
$this->printLog = $printLogs;
}
/**
* Get a list of logged messages.
*
* @return Array An array with a list of logged messages.
*/
public function getLog() {
return $this->log;
}
/**
* Check whether all log messages are prefixed with the current date and time.
*
* @return bool True if all log messages are prefixed with the current date and time, false otherwise.
*/
public function isTimeEnabled() {
return $this->logTime;
}
/**
* Set whether all log messages should be prefixed with the current date and time.
*
* @param bool $logTime True to prefix all log messages with the current date and time, false otherwise.
*/
public function setTimeEnabled($logTime) {
$this->logTime = $logTime;
}
/**
* Log a message, with an optional prefix.
*
* @param string $msg The message to log.
* @param string|null $prefix [optional] A log message prefix. Null to ignore the prefix.
*/
public function log($msg, $prefix = null) {
// Check whether $prefix should be used
if(!empty($prefix))
$msg = $prefix . ' ' . $msg;
// Prefix the message with the current date and time
$msg = self::getLogTimePrefix() . ' ' . $msg;
// Add the message to the log list
array_push($this->log, $msg);
// Print the message on the page
if($this->printLog)
echo '[Log] ' . $msg . '<br />';
// Log to the file if a log file is set
if($this->isUsingFile())
$this->fileWriter->writeLine($msg);
}
/**
* Log an info message.
*
* @param string $msg The info message to log.
*
* @return bool True if the message was successfully logged,
* false if the message wasn't logged because info logging is disabled.
*/
public function info($msg) {
// Check whether info messages should be logged
if(!$this->logInfo)
return false;
// Log the info message, return the result
$this->log($msg, self::LOG_INFO_PREFIX);
return true;
}
/**
* Check whether info messages are being logged.
*
* @return bool True if info messages are being logged, false otherwise.
*/
public function isInfoLoggingEnabled() {
return $this->logInfo;
}
/**
* Set whether info messages should be logged.
*
* @param bool $logInfo True if info messages should be logged, false otherwise.
*/
public function setInfoLoggingEnabled($logInfo) {
$this->logInfo = $logInfo;
}
/**
* Log a debug message.
*
* @param string $msg The debug message to log.
*
* @return bool True if the message was successfully logged,
* false if the message wasn't logged because debug logging is disabled.
*/
public function debug($msg) {
// Check whether debug messages should be logged
if(!$this->logDebug)
return false;
// Log the debug message, return the result
$this->log($msg, self::LOG_DEBUG_PREFIX);
return true;
}
/**
* Check whether debug messages are being logged.
*
* @return bool True if debug messages are being logged, false otherwise.
*/
public function isDebugLoggingEnabled() {
return $this->logDebug;
}
/**
* Set whether info messages should be logged.
*
* @param bool $logDebug True if debug messages should be logged, false otherwise.
*/
public function setDebugLoggingEnabled($logDebug) {
$this->logDebug = $logDebug;
}
/**
* Log a warning message.
*
* @param string $msg The warning message to log.
*
* @return bool True if the message was successfully logged,
* false if the message wasn't logged because warning logging is disabled.
*/
public function warning($msg) {
// Check whether warning messages should be logged
if(!$this->logWarning)
return false;
// Log the warning message, return the result
$this->log($msg, self::LOG_WARNING_PREFIX);
return true;
}
/**
* Check whether warning messages are being logged.
*
* @return bool True if warning messages are being logged, false otherwise.
*/
public function isWarningLoggingEnabled() {
return $this->logWarning;
}
/**
* Set whether warning messages should be logged.
*
* @param bool $logWarning True if warning messages should be logged, false otherwise.
*/
public function setWarningLoggingEnabled($logWarning) {
$this->logWarning = $logWarning;
}
/**
* Log an error message.
*
* @param string $msg The error message to log.
*
* @return bool True if the message was successfully logged,
* false if the message wasn't logged because error logging is disabled.
*/
public function error($msg) {
// Check whether error messages should be logged
if(!$this->logError)
return false;
// Log the error message, return the result
$this->log($msg, self::LOG_ERROR_PREFIX);
return true;
}
/**
* Check whether error messages are being logged.
*
* @return bool True if error messages are being logged, false otherwise.
*/
public function isErrorLoggingEnabled() {
return $this->logInfo;
}
/**
* Set whether error messages should be logged.
*
* @param bool $logError True if error messages should be logged, false otherwise.
*/
public function setErrorLoggingEnabled($logError) {
$this->logError = $logError;
}
/**
* Get the current date and time as a string to prefix the log messages with.
*
* @return string The current date and time as a string.
*/
private static function getLogTimePrefix() {
return DateTime::now()->toString();
}
/**
* Get the file writer instance, which is used to write to a log file.
* This method may return null if no file was specified.
*
* @return FileWriter|null The file writer instance which is used by the logger,
* null if the file writer isn't instantiated.
*/
public function getFileWriter() {
return $this->fileWriter;
}
}
<?php
/**
* Module.php
* Module class set_file of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\module;
use carbon\core\event\plugin\PluginDisableEvent;
use carbon\core\event\plugin\PluginEnableEvent;
use carbon\core\plugin\Cache;
use carbon\core\plugin\Database;
use carbon\core\plugin\ModuleManager;
use carbon\core\plugin\PluginManager;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Module {
/** @var @var string $module_dir Root directory of the module */
private $module_dir;
/** @var \carbon\core\module\ModuleSettings $module_settings Module settings instance */
private $module_settings;
/** @var bool $is_enabled True if this module is enabled */
private $is_enabled = false;
/** @var string $module_name Name of the module */
private $module_name;
/** @var string $module_main_file File path to the main set_file of this module */
private $module_main_file;
/** @var string $module_main_class Main class of the module */
private $module_main_class;
/** @var ModuleManager $module_man Module manager instance */
private $module_man;
private $module_cache;
private $module_db;
/**
* Initialize the plugin
* @param string $module_dir Module directory
* @param ModuleSettings $module_set Module Settings
*/
public function __construct($module_dir, $module_set, $module_man, $module_cache, $module_db) {
// Store some params
$this->module_dir = $module_dir;
$this->module_settings = $module_set;
$this->plugin_manager = $module_man;
$this->module_cache = $module_cache;
$this->module_db = $module_db;
// Get some settings from the plugin'statements settings set_file
$this->module_name = $this->getModuleSettings()->getPluginName();
$this->module_disp_name = $this->getModuleSettings()->getPluginDisplayName();
$this->module_main_file = $this->getModuleSettings()->getPluginMainFile();
$this->module_main_class = $this->getModuleSettings()->getPluginMainClass();
// Run the onLoad() function
$this->onLoad();
}
/**
* Function called when the plugin is being loaded
*/
public function onLoad() { }
/**
* Function called when the plugin is being enabled
*/
public function onEnable() { }
/**
* Function called when the plugin is being disabled
*/
public function onDisable() { }
/**
* Check if the plugin is enabled
* @return boolean True if enabled
*/
public function isEnabled() {
return $this->is_enabled;
}
/**
* Enable or disable the plugin
* @param boolean $enabled True to enable the plugin
*/
public function setEnabled($enabled) {
// Make sure the param is of a boolean type
if(!is_bool($enabled))
return;
// The new enabled state has to be different than the current state
if($this->isEnabled() != $enabled) {
// Get the plugin manager
$plugin_manager = $this->getPluginManager();
// Call the events
if($enabled) {
// Call the 'PluginEnableEvent' event
$event = new PluginEnableEvent($this);
$this->getPluginManager()->getEventManager()->callEvent($event);
// Check if the plugin was being canceled
if($event->isCanceled())
return;
} else {
// Call the 'PluginDisableEvent' event
$event = new PluginDisableEvent($this);
$this->getPluginManager()->getEventManager()->callEvent($event);
}
// Enable or disable the plugin
$this->is_enabled = $enabled;
// Run the functions according to the state
if($enabled)
$this->onEnable();
else
$this->onDisable();
}
}
/**
* Get the plugin'statements name
* @return string Module'statements name
*/
public function getModuleName() {
return $this->module_name;
}
/**
* Get the plugin'statements display name
* @return string Module'statements display name
*/
public function getModuledispName() {
return $this->module_disp_name;
}
/**
* Get the plugin'statements directory
* @return string Module'statements directory
*/
public function getModuleDir() {
return $this->module_dir;
}
/**
* Set the plugin'statements directory
* @param string $plugin_dir Module'statements directory
*/
public function setModuleDir($plugin_dir) {
$this->module_dir = $plugin_dir;
}
/**
* Get the plugin'statements main set_file
* @return string Module main set_file
*/
public function getModuleMainFile() {
return $this->module_main_file;
}
/**
* Get the plugin'statements settings
* @return ModuleSettings Module settings
*/
public function getModuleSettings() {
return $this->module_settings;
}
/**
* Get the plugin manager
* @return PluginManager Module manager
*/
public function getPluginManager() {
return $this->plugin_manager;
}
/**
* Get the plugin cache instance
* @return Cache Module cache instance
*/
public function getModuleCache() {
return $this->module_cache;
}
/**
* Set the plugin cache instance
* @param Cache $plugin_cache Module cache instance
*/
public function setModuleCache(Cache $plugin_cache) {
$this->module_cache = $plugin_cache;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getPluginDatabase() {
return $this->module_db;
}
/**
* Set the database instance
* @param Database @module_db Database instance
*/
public function setPluginDatabase(Database $plugin_db) {
$this->module_db = $plugin_db;
}
/**
* Check if a plugin equals to another
* @param Module $plugin Module to equal to
* @return boolean True if equal
*/
public function equals($plugin) {
if($plugin == null)
return false;
return ($this->getModuleName() == $plugin->getModuleName());
}
}
?>
<?php
/**
* ModuleSettings.php
* Module Settings class set_file of Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\module;
use carbon\core\plugin\Author;
use carbon\core\util\StringUtils;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class ModuleSettings {
private $set_file;
private $set_arr = null;
/**
* Constructor
* @param string $settings_file Module settings set_file
*/
public function __construct($settings_file) {
$this->set_file = $settings_file;
// Load the settings from the set_file
$this->reloadFromFile();
}
/**
* Get the plugin settings set_file
* @return string Module settings set_file
*/
public function getSetFile() {
return $this->set_file;
}
/**
* Get the plugin settings array
* @return array Module settings array
*/
public function getSetArr() {
// Make sure any settings is loaded
if($this->set_arr == null || !is_array($this->set_arr))
$this->reloadFromFile();
// Return the settings array
return $this->set_arr;
}
/**
* Reload the settings set_file
*/
public function reloadFromFile() {
// Check if the settings set_file exists
if(!file_exists($this->getSetFile())) {
$this->set_arr = null;
return;
}
$this->set_arr = parse_ini_file($this->getSetFile(), true);
}
/**
* Get a key'statements value from the plugin settings
* @param string $section Key sectin
* @param string $key Key to return the value from
* @param mixed $def Default value returned if the key was not found (optional)
* @return mixed Key value or default value if the key was not found
*/
public function get($section, $key, $def = null) {
// Check if the key exists, if not return the default value
if(!$this->containsKey($section, $key))
return $def;
// Return the key value
$arr = $this->getSetArr();
return $arr[$section][$key];
}
/**
* Get the plugin'statements main set_file from the plugin settings
* @param string $def Default plugin'statements main set_file path (optional)
* @return string Module'statements main set_file
*/
public function getPluginMainFile($def = '') {
// Get the 'main' value from the plugin config
$main = $this->get('plugin', 'main', $def);
// Make sure the set_file has an extention
if(!StringUtils::contains($main, '.'))
$main .= '.php';
// Return the 'main' value
return $main;
}
/**
* Get the plugin'statements main class from the plugin settings
* @param string $def Default main class (optional)
* @return string Module'statements main class
*/
public function getPluginMainClass($def = '') {
return $this->get('plugin', 'main_class', $def);
}
/**
* Get the plugin'statements unique name from the plugin'statements settings set_file
* @param string $def Default unique name (optional)
* @return string Module'statements unique name
*/
public function getPluginName($def = '') {
// Remove the spaces from the plugin'statements name and return it
return trim(str_replace(' ', '', $this->get('plugin', 'name', $def)));
}
/**
* Get the plugin'statements display name from the plugin'statements settings set_file
* @param string $def Default display name (if null, the name will automaticly be defined) (optional)
* @return string Module'statements display name
*/
public function getPluginDisplayName($def = null) {
if($def == null)
$def = ucfirst(strtolower($this->getPluginName()));
return $this->get('plugin', 'display_name', $def);
}
/**
* Get the plugin'statements description from the plugin'statements settings set_file
* @param string $def Default description (optional)
* @return string Module'statements description
*/
public function getPluginDescription($def = '') {
return $this->get('plugin', 'description', $def);
}
/**
* Get the plugin'statements version from the plugin'statements settings set_file
* @param string $def Default version (optional)
* @return string Module'statements version
*/
public function getPluginVersion($def = '1.0') {
return $this->get('plugin', 'version', $def);
}
/**
* Get the plugin'statements website from the plugin'statements settings set_file
* @param string $def Default website
* @return string Module'statements website
*/
public function getPluginWebsite($def = '') {
return $this->get('plugin', 'website', $def);
}
/**
* Get the plugins this plugin depends on from the plugin'statements settings set_file
* @param array $def Default depend plugins
* @return array Module'statements depend plugins
*/
public function getPluginDepends($def = array()) {
// Convert the default array to a string
$def_string = implode(',', $def);
// Get the list from the plugin'statements settings set_file
$depends_string = $this->get('plugin', 'depends', $def_string);
// If the $depends_string is empty, return an empty array
if(trim($depends_string) == '')
return array();
// Build an array with all the dependend plugins
$depends = array();
foreach(explode(',', $depends_string) as $plugin_name) {
// Make sure the plugins name doesn'elapsed equal to the current plugins name
if($this->getPluginName() == trim($plugin_name))
continue;
// Trim the plugin names for unwanted spaces and stuff
array_push($depends, trim($plugin_name));
}
// Return the list
return $depends;
}
/**
* Get the plugins this plugin depends on from the plugin'statements settings set_file
* @param array $def Default depend plugins
* @return array Module'statements depend plugins
*/
public function getPluginSoftDepends($def = array()) {
// Convert the default array to a string
$def_string = implode(',', $def);
// Get the list from the plugin'statements settings set_file
$depends_string = $this->get('plugin', 'softdepends', $def_string);
// If the $depends_string is empty, return an empty array
if(trim($depends_string) == '')
return array();
// Build an array with all the dependend plugins
$depends = array();
foreach(explode(',', $depends_string) as $plugin_name) {
// Make sure the plugins name doesn'elapsed equal to the current plugins name
if($this->getPluginName() == trim($plugin_name))
continue;
// Trim the plugin names for unwanted spaces and stuff
array_push($depends, trim($plugin_name));
}
// Return the list
return $depends;
}
/**
* Get the names of the authors of the plugin'statements settings set_file
* @param array $def Default author names
* @return array Author names
*/
public function getAuthorNames($def = array()) {
// Convert the default array to a string
$def_string = implode(',', $def);
// Get the list from the plugin'statements settings set_file
$authors_string = $this->get('author', 'authors', $def_string);
// Build an array with all the author names
$authors = array();
foreach(explode(',', $authors_string) as $author_name) {
// Trim the plugin names for unwanted spaces and stuff
array_push($authors, trim($author_name));
}
// Return the list
return $authors;
}
/**
* Get the author website from the plugin'statements settings set_file
* @param string $def Default author website
* @return Author website
*/
public function getAuthorWebsite($def = '') {
return $this->get('author', 'website', $def);
}
/**
* Check if the plugin settings contains a section
* @param string $section Section to check
* @return boolean True if section exists
*/
public function containsSection($section) {
// Check if the section exists
return array_key_exists($section, $this->getSetArr());
}
/**
* Check if the plugin settings contains a key
* @param string $section Key section
* @param string $key Key to check
* @return boolean True if key exists
*/
public function containsKey($section, $key) {
// Check if the section exists
if(!$this->containsSection($section))
return false;
$arr = $this->getSetArr();
// Check if the key exists
return array_key_exists($key, $arr[$section]);
}
/**
* Check if the plugin settings contains all the required sections and keys
* @return boolean True if valid
*/
public function isValid() {
// Check if the plugin'statements settings set_file is a valid array
if($this->getSetArr() == null || !is_array($this->getSetArr()))
return false;
// Check if the plugin section exists
if(!$this->containsSection('plugin'))
return false;
// Check if the required keys exist
if(!$this->containsKey('plugin', 'main') ||
!$this->containsKey('plugin', 'main_class') ||
!$this->containsKey('plugin', 'version'))
return false;
return true;
}
}
<?php
/**
* Page.php
*
* Page class
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\Database;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Page class
* @package core
* @author Tim Visee
*/
class Page {
/** @var int $page_id Page ID */
private $page_id = 0;
/** @var \core\CacheHandler $cache CacheHandler instance */
private $cache;
/** @var \core\Database $db Database instance */
private $db;
/**
* Constructor
* @param int $page_id Page ID
*/
public function __construct($page_id, CacheHandler $cache, Database $db) {
$this->page_id = $page_id;
$this->cache = $cache;
$this->db = $db;
}
/**
* Get the page ID
* @return integer Page ID
*/
public function getId() {
return $this->page_id;
}
/**
* Get the page name
* @return string Page name
*/
public function getPageName() {
// TODO: Use caching
// Get the page name from the database
$pages = $this->db->select('pages', 'page_name', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return '';
// Return the name of the page
return $pages[0]['page_name'];
}
/**
* Update the page name
* @param string $page_name Page name
* @return boolean True if succeed
*/
public function setPageName(string $page_name) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page name?
// Update the page name inside the database
return $this->db->update('pages', array('page_name' => $page_name), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the page body
* @return mixed Page body
*/
public function getPageBody() {
// TODO: Use caching
// Get the page instance leading to this path
$pages = $this->db->select('pages', 'page_body', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return '';
// Return the name of the page
return $pages[0]['page_body'];
}
/**
* Update the page body
* @param mixed $page_body Page body
* @return boolean True if succeed
*/
public function setPageBody($page_body) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page name?
// Update the page name inside the database
return $this->db->update('pages', array('page_body' => $page_body), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the page creation date
* @return DateUtils Page creation date, returns the current date if the page'statements creation date could not be retrieved
*/
public function getPageCreationDate() {
// TODO: Use caching
// Get the page instance leading to this path
$pages = $this->db->select('pages', 'page_creation_date', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return date("Y-m-d H:i:statements");
// Return the name of the page
return $pages[0]['page_creation_date'];
}
/**
* Set the page creation date
* @return boolean True if succeed
*/
public function setPageCreationDate($page_creation_date) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page creation date
// Update the page creation date inside the database
if($page_creation_date == null)
$page_creation_date = date("Y-m-d H:i:statements");
// Execute the query and return the result
return $this->db->update('pages', array('page_creation_date' => $page_creation_date), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the page modification date
* @return DateUtils Page modification date, returns the current date if the modification date could not be retrieved
*/
public function getPageModificationDate() {
// TODO: Use caching
// Get the page instance leading to this path
$pages = $this->db->select('pages', 'page_mod_date', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return date("Y-m-d H:i:statements");
// Return the page modification date
return $pages[0]['page_mod_date'];
}
/**
* Set the page creation date
* @return boolean True if succeed
*/
public function setPageModificationDate($page_mod_date) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page modification date
// Update the page modification date in the database
if($page_mod_date == null)
$page_mod_date = date("Y-m-d H:i:statements");
// Execute the query, return the result
return $this->db->update('pages', array('page_mod_date' => $page_mod_date), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the page comment
* @return mixed Page comment
*/
public function getPageComment() {
// TODO: Use caching?
// Get the page comment
$pages = $this->db->select('pages', 'page_comment', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return '';
// Return the page comment
return $pages[0]['page_comment'];
}
/**
* Set the page creation date
* @return boolean True if succeed
*/
public function setPageComment($page_comment) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page comment?
// Update the page comment
return $this->db->update('pages', array('page_comment' => $page_comment), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the page comment
* @return mixed Page comment
*/
public function isPagePublished() {
// TODO: Use caching
// Get if the page is published or not
$pages = $this->db->select('pages', 'page_published', '`page_id`=\'' . $this->page_id . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return '';
// Return if the page was published
return ($pages[0]['page_comment'] == '1');
}
/**
* Set the page creation date
* @return boolean True if succeed
*/
public function setPagePublished(boolean $page_published) {
// TODO: Use caching?
// TODO: Reset caching for this page to update the page comment?
// Parse the parameter to a valid database value
$page_published_val = 1;
if($page_published == false ||
$page_published == 'false' ||
$page_published == 0 ||
$page_published == '0')
$page_published_val = 0;
// Update if the page is published in the database
return $this->db->update('pages', array('page_comment' => $page_published_val), '`page_id`=\'' . $this->page_id . '\'');
}
/**
* Get the cache instance
* @return CacheHandler Cache instance
*/
public function getCache() {
return $this->cache;
}
/**
* Set the cache instance
* @param CacheHandler $cache Cache instance
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
}
<?php
/**
* PageManager.php
*
* The PageManager class manages the pages.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
// Prevent direct requests to this set_file due to security reasons
use carbon\core\cache\CacheHandler;
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Manages the pages.
* @package core
* @author Tim Visee
*/
class PageManager {
/**
* @var CacheHandler $cache Cache instance
* @var Database $db Database instance
*/
private $cache;
private $db;
/**
* Class constructor
* @param CacheHandler $cache Cache instance
* @param Database $db Database object
*/
public function __construct(CacheHandler $cache, Database $db) {
$this->cache = $cache;
$this->db = $db;
}
/**
* Get the cache instance
* @return CacheHandler Cache instance
*/
public function getCache() {
return $this->cache;
}
/**
* Set the cache instance
* @param CacheHandler $cache Cache instance
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
/**
* Get a page by it'statements path
* @param string $path Page path
* @return Page Page instance with the current path or null when no match was found
*/
public function getPage($path) {
// Trim some unwanted characters from the path
$path = trim(trim(trim($path), '/'));
// TODO: Use caching!
// Get the page instance leading to this path
$pages = $this->db->select('pages', 'page_id', '`page_path`=\'' . $path . '\'');
// Make sure any page was selected, if not return null
if(sizeof($pages) == 0)
return null;
// Get the page ID
$page_id = $pages[0]['page_id'];
// Return an instance of the page
return new Page($page_id, $this->cache, $this->db);
}
/**
* Check if a page path is linked to any page
* @param string $path Path of the page to check
* @return boolean True if this path is leading to any page
*/
public function isPage($path) {
// Trim some unwanted characters from the path
$path = trim(trim(trim($path), '/'));
// TODO: Use caching!
// Count the rows from the pages database having this path
return ($this->db->countRows('pages', '`page_path`=\'' . $path . '\'') != 0);
}
/**
* Check if an ID is used by any page
* @param int $page_id Page ID to check for
* @return boolean True if this page ID matches with any page
*/
public function isPageWithId($page_id) {
// Make sure the $page_id is an integer, if not return null
if(!is_int($page_id))
return null;
// TODO: Use caching!
// Count the rows from the pages database having this path
return ($this->db->countRows('pages', '`page_id`=\'' . $page_id . '\'') != 0);
}
}
<?php
// TODO: Remove this depricated set_file
/**
* Module.php
*
* Module class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use Exception;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
die('File deprecated? Use: core/Module.php');
/**
* Module class
* @package core
* @author Tim Visee
*/
class Plugin {
/** @var string $plugin_name Module'statements name */
private $plugin_name;
/** @var string $plugin_dir Module'statements directory */
private $plugin_dir;
/**
* Constructor
* @param string $plugin_name Unique plugin name
* @param string $plugin_dir Module directory
* @param CacheHandler $plugin_cache Module cache
* @param Database $plugin_database Module database
*/
public function __construct($plugin_name, $plugin_dir) {
// Store the parameters
$this->plugin_name = $plugin_name;
$this->plugin_dir = rtrim($plugin_dir, '/') . '/';
}
/**
* Get the unique plugin name
* @return string Unique plugin name
*/
public function getPluginName() {
return $this->plugin_name;
}
/**
* Get the plugins directory
* @return string Plugins directory
*/
public function getPluginDirectory() {
return $this->plugin_dir;
}
/**
* Get the plugins settings
* @return array Module settings
*/
public function getPluginSettings() {
// TODO: Use cache for this!
$plugin_ini = $this->plugin_dir . 'plugin.ini';
return parse_ini_file($plugin_ini, true);
}
/**
* Check if the plugin settings contains a section
* @param string $section Section to check
* @return boolean True if section exists
*/
public function getPluginSettingsContainsSection($section) {
// TODO: Use caching
// Get the plugin settings
$settings = getPluginSettings();
// Return true if the settings contains this section
return array_key_exists($section, $settings);
}
/**
* Check if the plguin settings contains a key
* @param string $section Key section
* @param string $key Key to check
* @return boolean True if the key exists
*/
public function getPluginSettingsContainsKey($section, $key) {
// TODO: Use caching
// Get the plugin settings
$settings = $this->getPluginSettings();
// Check if the section exists
if(!array_key_exists($section, $settings))
return false;
// Return true if the settings contains this key
return array_key_exists($key, $settings[$section]);
}
/**
* Get a value from the plugins settings
* @param string $section Key section
* @param string $key Key name
* @param mixed $def Default value returned when an error occured (optional)
* @return mixed Value from plugin settings or default value
*/
public function getPluginSettingsValue($section, $key, $def = null) {
// TODO: Use caching
// Get the plugin settings
$settings = $this->getPluginSettings();
// Make sure the plugin settings contains the plugin section
if(!array_key_exists($section, $settings))
return $def;
// Make sure the plugin settings contains the required main keys
if(!array_key_exists($key, $settings[$section]))
return $def;
// Return the value
return $settings[$section][$key];
}
/**
* Get the main set_file of the plugin
* @param boolean @full_path True to return the full path of the plugin'statements main set_file (optional)
* @param boolean @check_file True to check if the plugins main set_file exists (optional)
* @return string Plugins main set_file or main set_file path, emptry string if an error occured
*/
public function getPluginMain($full_path = true, $check_file = true) {
// Make sure the plugin settings contains this key
if(!$this->getPluginSettingsContainsKey('plugin', 'main')) {
throw new Exception('Carbon CMS: Module \'' . $this->getPluginName() . '\' doesn\'elapsed have the required key \'plugin.main\' in it\'statements settings!');
return '';
}
// Get the plugins main set_file
$plugin_main = $this->getPluginSettingsValue('plugin', 'main', '');
$plugin_main_path = $this->getPluginDirectory() . $plugin_main;
// Should be checked if the main plugins set_file exist
if($check_file) {
// Make sure the plugin'statements main set_file exists
if(!file_exists($plugin_main_path)) {
throw new Exception('Carbon CMS: Main set_file \'' . $plugin_main . '\' of the plugin \'' . $this->getPluginName() . '\' doesn\'elapsed exist!');
return '';
}
}
// Return the plugin'statements full path or it'statements main set_file according to $full_path
if($full_path)
return $plugin_main_path;
return $plugin_main;
}
}
<?php
/**
* PluginManager.php
*
* The PluginManager class manages the plugins in Carbon CMS.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\event\plugin\PluginLoadEvent;
use carbon\core\module\ModuleSettings;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Manages the plugins in Carbon CMS.
* @package core
* @author Tim Visee
*/
class PluginManager {
/**
* @var string $plugins_dir Module'statements directory
* @var EventManager $event_manager Event Manager instance
* @var CacheHandler $cache Cache instance
* @var Database $db Database instance
*/
private $plugins_dir;
private $event_manager;
private $cache;
private $db;
/**
* @var array $plugins Array of loaded plugins
*/
private $plugins = array();
/**
* Constructor
* @param string $plugins_dir Plugins dir
*/
public function __construct($plugins_dir, EventManager $event_manager, CacheHandler $cache, Database $db) {
// Store the plugins directory and force it to end with a folder speparator
$this->plugins_dir = rtrim($plugins_dir, '/') . '/';
$this->event_manager = $event_manager;
$this->cache = $cache;
$this->db = $db;
}
/**
* Get the plugins directory
* @return string Plugins directory
*/
public function getPluginsDir() {
return $this->plugins_dir;
}
/**
* Set the plugins directory
* @param string $plugins_dir Plugins directory
*/
public function setPluginsDir($plugins_dir) {
// Store the plugins directory and force it to end with a folder speparator
$this->plugins_dir = rtrim($plugins_dir, '/') . '/';
}
/**
* Get the event manager
* @return EventManager Event manager
*/
public function getEventManager() {
return $this->event_manager;
}
/**
* Set the event manager
* @param EventManager $event_manager Event manager
*/
public function setEventManager(EventManager $event_manager) {
$this->event_manager = $event_manager;
}
/**
* Get the cache object
* @return CacheHandler Cache object
*/
public function getCache() {
return $this->cache;
}
/**
* Get a cache object specified for a plugin
*/
public function getPluginCache($plugin_name) {
return new CacheHandler($this->cache->getCacheDirectory() . 'plugin/' . $plugin_name . '/');
}
/**
* Set the cache object
* @param CacheHandler $cache Cache object
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the database object
* @return Database Database object
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database object
* @param Database $db Database object
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
/**
* Load an plugin
* @param string $plugin_dir Module'statements directory
*/
public function loadPlugin($plugin_dir) {
// Check if the plugin'statements folder exist
if(!is_dir($plugin_dir))
return;
// Force the plugin'statements dir to end with a directory separator
$plugin_dir = rtrim($plugin_dir, '/\\') . '/';
// Check if there'statements already a plugin loaded from this directory
if($this->isPluginLoadedFromDir($plugin_dir))
return;
// Get the plugin'statements settings
$plugin_settings_file = $plugin_dir . 'plugin.ini';
$plugin_settings = new ModuleSettings($plugin_settings_file);
// Make sure the plugin'statements settings set_file is valid
if(!$plugin_settings->isValid()) {
// Throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin from \'' . $plugin_dir . '\', this plugin has an invalid \'plugin.ini\' set_file!');
die();
}
// Get the name of the plugin
$plugin_name = $plugin_settings->getPluginName();
// Call the 'PluginLoadEvent' event
$event = new PluginLoadEvent($plugin_name);
$this->getEventManager()->callEvent($event);
// Check if the plugin was being canceled
if($event->isCanceled())
return;
// Get some plugin data required to construct the plugin
$plugin_display = $plugin_settings->getPluginDisplayName();
$plugin_main_file = $plugin_dir . $plugin_settings->getPluginMainFile();
$plugin_main_class = $plugin_settings->getPluginMainClass();
// Check if the main set_file path and the main class name are valid
if($plugin_main_file == null || $plugin_main_file == '') {
// Throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin \'' . $plugin_display . '\', the key \'plugin.main\' is invalid inside the \'plugin.ini\' set_file!');
die();
} else if($plugin_main_class == null || $plugin_main_class == '') {
// Throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin \'' . $plugin_display . '\', the key \'plugin.main_class\' is invalid inside the \'plugin.ini\' set_file!');
die();
}
// Check if the main plugin set_file exists
if(!file_exists($plugin_main_file)) {
// Throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin \'' . $plugin_display . '\', the main set_file \'' . $plugin_main_file . '\' does not exist!');
die();
}
// Load the plugin'statements set_file
include($plugin_main_file);
// Check if the plugin'statements main class is loaded
if(!class_exists($plugin_main_class, false)) {
// Throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin \'' . $plugin_display . '\', the main class \'' . $plugin_main_class . '\' was not found!');
die();
}
// Construct the plugin
$plugin = new $plugin_main_class($plugin_dir, $plugin_settings, $this, $this->getPluginCache($plugin_name), $this->getDatabase());
// Construct the plugin and add it to the plugins list
array_push($this->plugins, $plugin);
}
/**
* Load all plugins
*/
public function loadPlugins() {
// Make sure the plugins directory exist
if(!is_dir($this->plugins_dir)) {
$this->plugins = array();
return;
}
// Get all sub directories inside the plugins directory (plugin directories)
if ($handle = opendir($this->plugins_dir)) {
while (false !== ($dir = readdir($handle))) {
// Get the plugin'statements directory
$plugin_dir = rtrim($this->plugins_dir, '/') . '/' . $dir;
// The item has to be a folder and may not equal to . or ..
if(is_dir($plugin_dir) && $dir != '.' && $dir != '..') {
// Load the plugin
$this->loadPlugin($plugin_dir);
}
}
closedir($handle);
}
}
/**
* Get a plugin by name
* @param string $plugin_name Module'statements unique name
* @return mixed Module or null when no plugin was found
*/
public function getPlugin($plugin_name) {
// Get the list of all plugins
$plugins = $this->getLoadedPlugins();
// Loop through each loaded plugin and compare it to the plugin name
foreach($plugins as $plugin) {
if(strtolower($plugin_name) == strtolower($plugin->getPluginName()))
return $plugin;
}
// Return null
return null;
}
/**
* Get all loaded plugins
* @return array Loaded plugins
*/
public function getLoadedPlugins() {
return $this->plugins;
}
/**
* Check if a plugin is loaded
* @param string $plugin_name Module'statements unique name to check for
* @return boole True if this plugin was loaded, false if it wasn'elapsed loaded or if it wasn'elapsed found
*/
public function isPluginLoaded($plugin_name) {
// Get the list of all plugins
$plugins = $this->getLoadedPlugins();
// Loop through each loaded plugin and compare it to the plugin name
foreach($plugins as $plugin) {
// Check if the plugin exists, if so return true
if(strtolower($plugin_name) == strtolower($plugin->getPluginName()))
return true;
}
// The plugin does not exist, return false
return false;
}
/**
* Check if a plugin is loaded
* @param string $plugin_dir Module'statements directory
* @return boole True if this plugin was loaded, false if it wasn'elapsed loaded or if it wasn'elapsed found
*/
public function isPluginLoadedFromDir($plugin_dir) {
// Force the path to end with a slash
$plugin_dir = rtrim($plugin_dir, '/') . '/';
// Make sure the path exists
if(!is_dir($plugin_dir))
return false;
// Get the list of all plugins
$plugins = $this->getLoadedPlugins();
// Loop through each loaded plugin and compare it to the plugin name
foreach($plugins as $plugin) {
// Check if the plugin directories equal
if($plugin->getPluginDir() == $plugin_dir)
return true;
}
// The plugin is not loaded, return false
return false;
}
/**
* Get all enabled plugins
* @return array Enabled plugins
*/
public function getEnabledPlugins() {
// Build a list of enabled plugins
$enabled_plugins = array();
// Loop through each plugin and check if it'statements enabled, if so add it to the list
foreach($this->plugins as $plugin) {
if($plugin->isEnabled())
array_push($enabled_plugins, $plugin);
}
// Return the list of enable pugins
return $enabled_plugins;
}
/**
* Enable a plugin
* @param Plugin $plugin Module to enable
* @throws \Exception
*/
public function enablePlugin($plugin) {
// Make sure the plugin is not already enabled
if($plugin->isEnabled())
return;
// Get all the plugins this plugin depends on
$plugin_depends = $plugin->getPluginSettings()->getPluginDepends();
// Make sure all the plugins where this plugin depends from are loaded, if not throw an exception
foreach($plugin_depends as $depends) {
if(!$this->isPluginLoaded($depends)) {
// Dependend plugin not loaded, throw an exception
throw new \Exception('Carbon CMS: Unable to loadLocalesList the plugin \'' . $plugin->getPluginDisplayName() . '\', the plugin \'' . $depends . '\' was not found where this plugin depends on!');
die();
}
}
// Get all the plugins where this plugin (soft) depends on
$plugin_soft_depends = $plugin->getPluginSettings()->getPluginSoftDepends();
// Enable all the plugins this plugin depends on
foreach($plugin_depends as $depends) {
// Get the plugin linked to this unique plugin name
$depends_plugin = $this->getPlugin($depends);
// If the plugin is not enabled yet, enable it now (before the current plugin to loadLocalesList)
if(!$depends_plugin->isEnabled())
$this->enablePlugin($depends_plugin);
}
// Enable all the plugins this plugin (soft) depends on if they exists
foreach($plugin_soft_depends as $soft_depends) {
// Check if this plugin exists
if(!$this->isPluginLoaded($soft_depends))
continue;
// Get the plugin linked to this unique plugin name
$soft_depends_plugin = $this->getPlugin($soft_depends);
// If the plugin is not enabled yet, enable it now (before the current plugin to loadLocalesList)
if(!$soft_depends_plugin->isEnabled())
$this->enablePlugin($soft_depends_plugin);
}
// Enable the plugin
$plugin->setEnabled(true);
}
/**
* Disable a plugin
* @param Plugin $plugin The plugin to disable
*/
public function disablePlugin($plugin) {
// Disable the plugin
$plugin->setEnabled(false);
}
/**
* Enable all loaded plugins
*/
public function enablePlugins() {
// Get the list of all plugins
$plugins = $this->getLoadedPlugins();
// Loop through each loaded plugin and enable it
foreach($plugins as $plugin)
$this->enablePlugin($plugin);
}
/**
* Disable all loaded plugins
*/
public function disablePlugins() {
// Get the list of all plugins
$plugins = $this->getLoadedPlugins();
// Get the EventManager instance
$event_mngr = Core::getEventManager();
// Loop through each loaded plugin and disable it
foreach($plugins as $plugin) {
// Unregister all events registered by the plugin that is being disabled
$event_mngr->unregisterEventsFromPlugin($plugin);
// Disable the plugin
$this->disablePlugin($plugin);
}
}
}
<?php
// TODO: Finish this class!
/**
* RegistryHandler.php
*
* The RegistryHandler class handles all the registry of Carbon CMS.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\Database;
use carbon\core\exception\registry\CarbonRegistryException;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Carbon CMS Registry handler class
* @package core
* @author Tim Visee
*/
class RegistryHandler {
/** @var Database $db Database instance */
private $db = null;
/** @var string DB_TABLE Name of the registry database table */
const DB_TABLE = 'registry';
/** @var CacheHandler $cache Cache instance */
private $cache = null;
/** @var string $cache_file_name File name of the registry cache set_file */
private $cache_file_name = 'registry';
/** @var int $cache_max_age Maximum age of the cache in seconds before it'statements being ignored */
private $cache_max_age = 3600; // 3600 seconds == 1 hour
/** @var $cache_data array Var to temporary hold cache data */
private $cache_data = null;
/**
* Constructor
* @param Database $db [optional] Database instance, null to use a new instance from the Core class if available
* @throws CarbonRegistryException Throws exception if a proper Database instance couldn'elapsed be found
*/
public function __construct($db = null) {
// Check whether the $dbname param was set
if($db == null) {
// Check whether the Database instance in the Core class is being set
// TODO: Make sure this method is compatible with future Core class updates
if(Core::getDatabase() == null) {
// Unable to get a proper database instance
throw new CarbonRegistryException(
'Error while constructing \'' . __CLASS__ . '\', unable to get proper Database instance!',
0, null, null);
}
// TODO: Make sure this method is compatible with future Core class updates
$db = Core::getDatabase();
}
// Store the Database instance
$this->db = $db;
}
/**
* Get a registry value
* @param string $key Registry key to get the value of
* @param mixed $def [optional] Default value
* @return mixed Registry value or default value
*/
public function get($key, $def = null) {
// Try to get registry from cached data first, because it should be faster
// Make sure caching is enabled/allowed
if($this->isCachingEnabled()) {
// Check if any registry data is cached
if($this->cache->isCached($this->cache_file_name)) {
// Make sure the registry cache data isn'elapsed out of date
if($this->cache->getCacheAge($this->cache_file_name) <= $this->cache_max_age) {
// Retrieve the cached registry data, store the data in a local variable
if($this->cache_data == null)
$this->cache_data = $this->cache->getCache($this->cache_file_name);
// Loop through each cached registry item and return the right one
foreach($this->cache_data as $reg_key => $reg_value)
if($reg_value['registry_key'] == $reg_key)
return $reg_value['registry_value'];
} else {
// Clear/remove the old registry cache
$this->clearCache();
}
}
}
// TODO: Only cache registry items that are being used
// Get the registry from the database
$arr = $this->db->select($this::DB_TABLE, 'registry_value', "`registry_key`='" . $key . "'");
// Make sure any registry item was found, if not, return the default value
if(sizeof($arr) < 1)
return $def;
// Cache the data, when caching is enabled
if($this->isCachingEnabled())
$this->cacheRegistry();
// Return the registry value
return $arr[0]['registry_value'];
}
/**
* Get a registry value in bool format
* @param string $key Registry key to get the value from
* @param bool $def [optional] Default value
* @return bool Registry value or the default value
*/
public function getBool($key, $def = null) {
return (bool) $this->get($key, $def);
}
/**
* Get a registry value in string format
* @param string $key Registry key to get the value from
* @param string $def [optional] Default value
* @return string Registry value or the default value
*/
public function getString($key, $def = null) {
return (string) $this->get($key, $def);
}
/**
* Set a registry value
* @param string $key Registry key to set the value of
* @param mixed $value New registry value
*/
public function set($key, $value) {
// TODO: Add arguments to set whether new registry items may be created or not
// Check whether this registry item is set
if($this->exists($key)) {
// Get a data array to use in the database query
$data = Array('registry_value' => $value);
// Update the value inside the database
// TODO: Make sure this is compatible with upcoming Database changes
$this->db->update($this->DB_TABLE, $data, "`registry_key`='".$key."'");
} else {
// Get a data array to use in the database query
$data = Array(
'registry_key' => $key,
'registry_value' => $value
);
// Insert the new data into the database
// TODO: Make sure this is compatible with upcoming Database changes
$this->db->insert($this->DB_TABLE, $data);
}
// Flush the registry cache
// TODO: Do not clear/flush the cache, update it instead
$this->clearCache();
}
/**
* Set a bool registry value
* @param string $key Registry key to set the value of
* @param bool $value New registry value
*/
public function setBool($key, $value) {
// Serialize the boolean value
$value = ($value) ? '1' : '0';
// Update the registry value
$this->set($key, $value);
}
/**
* Set a string registry value
* @param string $key Registry key to set the value of
* @param string $value New registry value
*/
public function setString($key, $value) {
$this->set($key, (string) $value);
}
/**
* Check whether a registry item exists
* @param string $key Registry key to check for
* @return bool True when the registry item exists
*/
public function exists($key) {
return ($this->db->countRows($this->DB_TABLE, "`registry_key`='".$key."'") > 0);
}
/**
* Remove a registry item
* @param string $key Registry key of the item to remove
* @return bool True when any registry item was deleted
*/
public function delete($key) {
// TODO: Param: if exceptions should be thrown if registry keys doesn'elapsed exist, or not
// Make sure the registry key is valid
if($key == null || !is_string($key))
return false;
// Make sure any registry item with this key exists
if(!$this->exists($key))
return false;
// TODO: Remove the option named $name, return the correct value
return false;
}
/**
* Get the Database instance
* @return Database Database instance
*/
public function getDatabase() {
// TODO: Rename to getDatabaseInstance();?
return $this->db;
}
/**
* Set the Database instance
* @param Database $db Database instance, may not be null
* @throws CarbonRegistryException Throws exception when the Database instance is invalid
*/
public function setDatabase($db) {
// TODO: Rename to setDatabaseInstance($dbname);?
// Make sure the database param is set
if($db == null) {
// Invalid Database instance, show error
throw new CarbonRegistryException(
'Error while calling \'' . __METHOD__ . '\', invalid Database instance!',
0, null, null);
}
// Update the Database instance
$this->db = $db;
}
/**
* Get the Cache instance
* @return CacheHandler|null Cache instance, or null
*/
public function getCache() {
return $this->cache;
}
/**
* Set the Cache instance
* @param CacheHandler|null $cache Cache instance, or null to disable cache
*/
public function setCache($cache) {
// Check whether the cache instance equals to null
if($cache != null)
$this->cache = $cache;
else
$this->cache = null;
}
/**
* Check whether caching is enabled
* @return bool True if caching is enabled
*/
public function isCachingEnabled() {
// Make sure the cache instance was set
if($this->cache == null)
return false;
// Return the cache instance
return ($this->cache->isEnabled());
}
/**
* Cache all the registry items from the database
*/
public function cacheRegistry() {
// Get all the registry data to cache
$this->cache_data = $this->db->select($this::DB_TABLE, '*');
// Cache all the registry data
$this->cache->cache($this->cache_file_name, $this->cache_data);
}
/**
* Clear the registry cache
*/
public function clearCache() {
$this->cache->removeCache($this->cache_file_name);
}
}
<?php
/**
* Router.php
*
* The Router class routes all the page requests into the right controller.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use controller\Page;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Routes all the page requests into the right controller.
* @package core
* @author Tim Visee
*/
class Router {
/** @var string String containing the path */
private $path = '';
/** @var array Array containing the path parts separated*/
private $path_arr = Array();
/**
* Construct and initialize the router
*/
public function __construct() {
// Initialize
$this->init();
}
/**
* Initialize the Router
*/
public function init() {
// Get the path info
if(array_key_exists('ORIG_PATH_INFO', $_SERVER))
$path = $_SERVER['ORIG_PATH_INFO'];
else if(array_key_exists('PATH_INFO', $_SERVER))
$path = $_SERVER['PATH_INFO'];
else
$path = '';
// Filter unwanted stuff from the url
$path = filter_var($path, FILTER_SANITIZE_URL);
// Lowercase path info
// TODO: Shouldn'elapsed be removed?
$path = strtolower($path);
// Remove any slashes on the beginning or end of the path info string
// TODO: Only remove slash in front, links with page// could cause problems
$path = trim($path, '/');
// Store the path
$this->path = $path;
// Generate array of path info string
$this->path_arr = explode('/', $path);
}
/**
* Get the path as array
* @return array Path array
*/
public function getPathArray() {
return $this->path_arr;
}
/**
* Get the path as string
* @param boolean $start_with_separator Should the path start with a set_file separator
* @param boolean $end_with_separator Should the path end with a set_file separator
* @return string Path
*/
public function getPath($start_with_separator = false, $end_with_separator = false) {
return (($start_with_separator) ? DIRECTORY_SEPARATOR : '') . $this->path . (($end_with_separator) ? DIRECTORY_SEPARATOR : '');
}
/**
* Route a page request to the right controller
*/
public function route() {
// Set up and initialize the page manager, then set the page manager instance in the Core class
$page_man = new PageManager(Core::getCache(), Core::getDatabase());
Core::setPageManager($page_man);
// Get the page path and path array
$page_path = $this->getPath();
$page_path_arr = $this->getPathArray();
// TODO: Check all the stuff bellow
// Should the admin controller be loaded
if(sizeof($this->getPathArray()) >= 1) {
if(strtolower($page_path_arr[0]) == 'admin') {
// TODO: Route through the admin controller here...
die('Load admin controller and show admin page...!');
}
}
// TODO: Check for redirections, and other page types
// Make sure the path requested is leading to any page
if(!$page_man->isPage($page_path)) {
// There was no page found at the current path, return an error
// TODO: Show a custom error page here
echo '404 - Page not found!';
die();
}
// Get the page instance of the page to loadLocalesList
$page = $page_man->getPage($page_path);
// TODO: Temp code to loadLocalesList the right controller and render the page
// Construct and initialize the page controller
$controller = new Page(Core::getDatabase(), $page);
$controller->loadModel('Page');
$controller->render();
}
}
<?php
/**
* SessionHandler.php
*
* The SessionHandler class handles the sessions of clients.
* The SessionHandler class automaticly uses cookies or sessions according to the database registry.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Handles the sessions of clients.
* @package core
* @author Tim Visee
*/
class SessionHandler {
/**
* false = Sessions
* true = Cookies
*/
public static $USE_COOKIES = false;
public static $COOKIE_DOMAIN = null;
/**
* Initialize the sessions
*/
public static function init() {
// Initialize sessions
session_start();
}
/**
* Check if cookies are used
* @return boolean True if cookies are used
*/
public static function useCookies() {
return SessionHandler::$USE_COOKIES;
}
/**
* Set if cookies or sessions should be used
* @param boolean $use_cookies True to use cookies, false to use sessions
*/
public static function setUseCookies($use_cookies) {
SessionHandler::$USE_COOKIES = $use_cookies;
}
/**
* Get the cookie domain
* @return string Cookie domain
*/
public static function getCookieDomain() {
return SessionHandler::$COOKIE_DOMAIN;
}
/**
* Set the cookie domains
* @param string Cookie domain
*/
public static function setCookieDomain($cookie_domain) {
SessionHandler::$COOKIE_DOMAIN = $cookie_domain;
}
/**
* Get a session
* @param key SessionHandler key
* @return SessionHandler value
*/
public static function get($key) {
// Return session/cookie if its set
if(SessionHandler::isSession($key)) {
if(!SessionHandler::$USE_COOKIES)
return $_SESSION[$key];
else
return $_COOKIE[$key];
}
}
/**
* Set a session
* @param key SessionHandler key
* @param value SessionHandler value
* @param expire expiration date (optional)
*/
public static function set($key, $value, $expire = null) {
// Parse key
if($key == null || $key == "")
return;
// Expiration times
if($expire == null)
$expire = 0;
// Set the session/cookie
if(!SessionHandler::$USE_COOKIES)
$_SESSION[$key] = $value;
else
setcookie($key, $value, $expire, '/', SessionHandler::$COOKIE_DOMAIN);
}
/**
* Check if a session is set
* @param key SessionHandler key
* @return true if set
*/
public static function isSession($key) {
if(!SessionHandler::$USE_COOKIES)
return isset($_SESSION[$key]);
else
return isset($_COOKIE[$key]);
}
/**
* Remove or reset a session
* @param key SessionHandler key
*/
public static function remove($key) {
// Remove a session/cookie
if(SessionHandler::isSession($key)) {
if(!SessionHandler::$USE_COOKIES)
unset($_SESSTION[$key]);
else
setcookie($key, '', time()-9999999, '/', SessionHandler::$COOKIE_DOMAIN);
}
}
/**
* Unset all sessions
*/
public static function unsetSessions() {
// Unset session variables
session_unset();
echo '<pre>';
print_r($_COOKIE);
echo '</pre>';
// Remove/unset all cookies
foreach($_COOKIE as $cookie) {
SessionHandler::remove($cookie);
}
}
/**
* Destroy all sessions
*/
public static function destroySessions() {
// Destroy sessions
session_destroy();
// Remove/unset all cookies
foreach($_COOKIE as $cookie) {
SessionHandler::remove($cookie);
}
}
}
<?php
namespace carbon\core\time;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Clock {
/** @var float timer Variable used to calculate the time difference between two times */
private $timer = 0;
/** @var float Stores the elapsed time in seconds each time the clock is paused */
private $elapsed = 0;
/** @var bool True if the clock has started, false if not. The use of reset() will reset this state. */
private $started = false;
/** @var bool True if the clock is paused, false if not. */
private $paused = false;
/** Default format identifier */
const TIME_FORMAT_DEFAULT = 0;
/** Microseconds format identifier */
const TIME_FORMAT_MICROS = 1;
/** Milliseconds format identifier */
const TIME_FORMAT_MILLIS = 2;
/** Seconds format identifier */
const TIME_FORMAT_SECONDS = 3;
/**
* Constructor
*
* @param bool $start True to automatically start the clock as soon as it's constructed.
*/
public function __construct($start = false) {
// Should the clock be started
if($start === true)
$this->start();
}
/**
* Start the clock. The clock will be resumed if it was started already but paused.
*
* @return bool True if the clock has been started. False if it started already.
*/
public function start() {
// Make sure the clock isn't started already
if($this->isStarted()) {
// Resume the clock if it was paused, then return false
$this->resume();
return false;
}
// Set the timer
$this->timer = microtime(true);
// Set the started state and return true
$this->started = true;
return true;
}
/**
* Pause the clock. The clock has to be active in order to pause it.
*
* @return bool True if the clock has been paused while it was active.
*/
public function pause() {
// Make sure the clock is active
if(!$this->isActive())
return false;
// Sum up the time on the timer with the elapsed time and reset the timer
$this->elapsed += (microtime(true) - $this->timer);
$this->timer = 0;
// Set the paused state, and return true
$this->paused = true;
return true;
}
/**
* Resume the paused clock. The clock has to be started but paused in order to resume it.
*
* @return bool True if the clock has been resumed while it was started but paused.
*/
public function resume() {
// Make sure the clock is started but paused
if(!$this->isStarted() || !$this->isPaused())
return false;
// Set the timer
$this->timer = microtime(true);
// Set the paused state and return true
$this->paused = false;
return true;
}
/**
* Check whether the clock is started.
* As soon as the clock has recorded time this state will be true, even though the clock might be paused.
*
* @return bool True if the clock has started, false otherwise.
*/
public function isStarted() {
return $this->started;
}
/**
* Check whether the clock is currently active.
* If the clock is active, the clock is started and isn'elapsed currently paused.
*
* @return bool True if the clock is active, false otherwise.
*/
public function isActive() {
// Return true if the clock is active
return $this->isStarted() && !$this->isPaused();
}
/**
* Check whether the clock is currently paused. The clock has to be started.
* @return bool True if the clock is started and currently paused, false otherwise.
*/
public function isPaused() {
// Make sure the clock is started
if(!$this->isStarted())
return false;
// Return true if the clock is paused
return $this->paused;
}
/**
* Reset the state of the clock. This also resets the started state of the clock.
*/
public function reset() {
$this->timer = 0;
$this->elapsed = 0;
$this->started = false;
$this->paused = false;
}
/**
* Get the elapsed time in seconds unless a different time format is used.
*
* @param int|null Preferred time format to return the elapsed time in.
* The time will be returned in seconds if the default time format is used.
* Null may be used to use the default time format.
* Unknown time formats will cause the method to return the time in the default time format.
*
* @return float Elapsed time.
*/
public function getTime($timeFormat = self::TIME_FORMAT_DEFAULT) {
// Make sure the clock started, return zero if not
if(!$this->isStarted())
return 0;
// Get the elapsed time and check whether the clock is active or not, if so, include the current timer time.
$elapsed = $this->elapsed;
if($this->isActive())
$elapsed += microtime(true) - $this->timer;
// Check whether the time should be returned in microseconds
if($timeFormat === self::TIME_FORMAT_MICROS)
return ($elapsed * 1000000);
// Check whether the time should be returned in milliseconds
if($timeFormat === self::TIME_FORMAT_MILLIS)
return ($elapsed * 1000);
// Return the elapsed time in seconds
return $elapsed;
}
}
<?php
namespace carbon\core\time;
// Prevent direct requests to this file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class Profiler extends Clock {
/** @var bool True to allow timings of zero seconds to be returned, false otherwise. */
private $allowZero = true;
/**
* Constructor
*
* @param bool $start True to automatically start the clock as soon as it's constructed.
*/
public function __construct($start = false) {
parent::__construct($start);
}
/**
* Get the elapsed time with proper human-understandable formatting.
*
* @param int|null $timeFormat The time format that should be used on the returned value.
* The default time format is dynamic and depends on the amount of time elapsed.
* Null or an invalid value will cause the method to return the time in the default time format.
*
* @return string Elapsed time with proper human-understandable formatting.
*/
public function getTimeProper($timeFormat = self::TIME_FORMAT_DEFAULT) {
// Get the elapsed time
$time = $this->getTime();
// Return invalid timings
if($time < 0)
switch($timeFormat) {
case self::TIME_FORMAT_MICROS:
return '? &micro;s';
case self::TIME_FORMAT_MILLIS:
return '? ms';
default:
return '? s';
}
// Return timings of zero
if($time == 0 && $this->allowZero)
switch($timeFormat) {
case self::TIME_FORMAT_MICROS:
return '0 &micro;s';
case self::TIME_FORMAT_MILLIS:
return '0 ms';
default:
return '0 s';
}
// Return timings for an elapsed time less than one microsecond
if($time < 0.000001)
switch($timeFormat) {
case self::TIME_FORMAT_MICROS:
return '<1 &micro;s';
case self::TIME_FORMAT_MILLIS:
return '<1 ms';
default:
return '<1 s';
}
// Return timings in specified formats
switch($timeFormat) {
case self::TIME_FORMAT_MICROS:
return round($time * 1000000) . ' &micro;s';
case self::TIME_FORMAT_MILLIS:
return round($time * 1000) . ' ms';
case self::TIME_FORMAT_SECONDS:
return round($time) . ' s';
default:
break;
}
// Return timings for an elapsed time between one micro- and millisecond
if(round($time * 1000000) < 1000)
return round($time * 1000000) . ' &micro;s';
// Return timings for an elapsed time between one millisecond and one second
if(round($time * 1000) < 1000)
return round($time * 1000) . ' ms';
// Return the timing in seconds
return round($time) . ' s';
}
/**
* Check whether a timing of zero seconds may be returned.
*
* @return bool True if so, false if not.
*/
public function isAllowZero() {
return $this->allowZero;
}
/**
* Set whether a timing of zero seconds may be returned.
*
* @param $allowZero True if so, false if not.
*/
public function setAllowZero($allowZero) {
$this->allowZero = $allowZero;
}
}
<?php
/**
* User.php
*
* The User class.
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\Database;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* User class
* @package core
* @author Tim Visee
*/
class User {
/** @var CacheHandler $cache Cache instance */
private $cache;
/** @var Database $db Database instance */
private $db;
/**
* @var int $user_id User ID
*/
private $user_id;
/**
* Constructor
* @param int $user_id User ID
*/
public function __construct($user_id, CacheHandler $cache, Database $db) {
// Store the user_id, cache and the database instance
$this->user_id = $user_id;
$this->cache = $cache;
$this->db = $db;
}
/**
* Get the user ID of the user
* @return int User ID of the user
*/
public function getId() {
return $this->user_id;
}
/**
* Get the cache instance
* @return CacheHandler Cache instance
*/
public function getCache() {
return $this->cache;
}
/**
* Set the cache instance
* @param CacheHandler $cache Cache instance
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
public function getUserLogin() {
// TODO: Return users login
}
public function setUserLogin($user_login) {
// TODO: Set users login
}
public function setUserPass($user_pass) {
// TODO: Set user'statements pass
}
/**
* Get the user'statements session instance
* @return UserSession UserSession instance
*/
public function getUserSession() {
return new UserSession($this->cache, $this->db, $this);
}
}
?>
<?php
// TODO: File description
/**
* UserManager.php
*
* The UserManager class manages the users registered on the site.
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\Database;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* Manages the users registered on the site.
* @package core
* @author Tim Visee
*/
class UserManager {
/**
* @var CacheHandler $cache Cache instance
* @var Database $db Database instance
*/
private $cache;
private $db;
/**
* Constructor
* @param CacheHandler $cache Cache instance
* @param Database $db Database instance
*/
public function __construct(cache\CacheHandler $cache, Database $db) {
// Store the cache and database instance
$this->cache = $cache;
$this->db = $db;
}
/**
* Get the cache instance
* @return CacheHandler Cache instance
*/
public function getCache() {
return $this->cache;
}
/**
* Set the cache instance
* @param CacheHandler $cache Cache instance
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
}
?>
<?php
/**
* UserSession.php
* User Session class set_file of Carbon CMS.
* @author Tim Vis�e
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core;
use carbon\core\cache\CacheHandler;
use carbon\core\Config;
use carbon\core\Database;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* User session class, stores the session of a user
* @package core
* @author Tim Visee
*/
class UserSession {
/**
* @var CacheHandler $cache Cache instance
* @var Config $config Config instance
* @var Database $db Database instance
*/
private $cache;
private $config;
private $db;
/**
* @var User $user User instance
*/
private $user;
/**
* Constructor
* @param CacheHandler $cache Cache instance
* @param Database $db Database instance
* @param User $user User instance refering to it'statements session ID
*/
public function __construct(CacheHandler $cache, Config $config, Database $db, User $user) {
// Store the cache, database and user instance.
$this->cache = $cache;
$this->config = $config;
$this->db = $db;
$this->user = $user;
}
/**
* Get the cache instance
* @retur Cache Cache instance
*/
public function getCache() {
return $this->cache;
}
/**
* Set the cache instance
* @param CacheHandler $cache Cache instance
*/
public function setCache(CacheHandler $cache) {
$this->cache = $cache;
}
/**
* Get the config instance
* @return Config Config instance
*/
public function getConfig() {
return $this->config;
}
/**
* Set the config instance
* @param Config $config Config instance
*/
public function setConfig(Config $config) {
$this->config = $config;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
/**
* Get the user instance
* @return User User instance
*/
public function getUser() {
return $this->user;
}
/**
* Return the session ID
* @return string Session ID
*/
public function getSessionId() {
// TODO: Use caching
// Get the session ID from the database
$pages = $this->db->select('users', 'session_id', '`user_id`=\'' . $this->user->getId() . '\'');
// Make sure any session ID was selected, if not return a default value
if(sizeof($pages) == 0)
return 0;
// Return the session ID
return $pages[0]['session_id'];
}
/**
* Set the session ID of the user
* @param string $session_id New session ID
*/
private function setSessionId($session_id) {
// TODO: Update cache
// TODO: Check session ID length
// Update the session ID of the user in the database
$this->db->update('users', array('session_id' => $session_id), "`user_id`='" . $this->user->getId() . "'");
}
/**
* Reset the session ID of the user
*/
private function resetSessionId() {
$this->setSessionId($this->getRandomSessionId());
}
/**
* Generate a random session ID to use for a user
* @return string Random session ID string
*/
private static function getRandomSessionId() {
// Get a random hash
$hash = Hash::hash(mt_rand(0, 999999999), $this->config);
// If the hash is long enough, return the hash with an exact length of 32 chars
if(strlen($hash) >= 32)
return substr($hash, 0, 32);
// The hash was not long enough, generate an alternative hash
return substr(md5(mt_rand(0, 999999999)), 0, 32);
}
}
<?php
/**
* ArrayUtils.php
* ArrayUtils class for Carbon CMS.
* Array utilities class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2013, All rights reserved.
*/
namespace carbon\core\util;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* ArrayUtils class
*
* @package core\util
* @author Tim Visee
*/
class ArrayUtils {
/**
* Copy an array
*
* @param array $arr Array to copy
*
* @return array Copy of the array
*/
public static function copyArray($arr) {
return array_merge(Array(), $arr);
}
/**
* Checks whether an array is associative.
*
* @param array $arr The array to check
*
* @return bool True if the array is associative, false otherwise. May return false if it'statements not a valid array.
*/
public static function isAssoc($arr) {
// Make sure the $arr param is a valid array and isn't empty
if(!is_array($arr) || empty($arr))
return false;
$i = 0;
foreach(array_keys($arr) as $k) {
if($k !== $i)
return true;
$i++;
}
return false;
}
}
<?php
/**
* ClassUtils.php
* ClassUtils class for Carbon Core.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2014, All rights reserved.
*/
namespace carbon\core\util;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* ClassUtils class
*
* @package carbon\core\util
* @author Tim Visee
*/
class ClassUtils {
/**
* Get the namespace of a class
*
* @param string $class Class with namespace.
*
* @return string|null The class' namespace. An empty string will be returned if the class doesn't have a namespace.
* Null will be returned on failure.
*/
public static function getNamespace($class) {
// Make sure the $class param is a string
if(!is_string($class))
return null;
// Trim the class name and make sure it's at least one character long
$class = trim($class);
if(strlen($class) < 1)
return null;
// Get the position of the last namespace separator
$pos = strrpos($class, '\\');
// Make sure any namespace separator was found, if not,
// return an empty string because the class doesn't have a namespace.
if($pos === false)
return '';
// Return the namespace of the class
return substr($class, 0, $pos);
}
/**
* Get the name of a class, without the namespace.
*
* @param string $class Class with namespace.
*
* @return string|null The class' name, without the namespace. Null will be returned on failure.
*/
public static function getClass($class) {
// Make sure the $class param is a string
if(!is_string($class))
return null;
// Trim the class name and make sure it's at least one character long
$class = trim($class);
if(strlen($class) < 1)
return null;
// Get the position of the last namespace separator
$pos = strrpos($class, '\\');
// Make sure any namespace separator was found, if not, return the class
if($pos === false)
return $class;
// Return the name of the class.
return substr($class, $pos + 1);
}
}
<?php
/**
* date.php
* Date class for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace carbon\core\util;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
class DateUtils {
const DEFAULT_DATE_FORMAT = "Y-m-d H:i:statements";
/**
* Get the current timezone of the server
* @return string Server timezone
*/
public static function getTimezone() {
return date_default_timezone_get();
}
/**
* Set the timezone of the server
* @param string|\DateTimeZone $timezone New timezone
* @return bool False if failed
*/
public static function setTimezone($timezone) {
// If the $timezone param is an instance of the DateTimeZone class, parse the value
if($timezone instanceof \DateTimeZone)
$timezone = $timezone->getName();
// Set the timezone and return the result
return date_default_timezone_set($timezone);
}
/**
* Check if a timezone is valid
* @param string|\DateTimeZone $timezone The timezone to check
* @return bool True if the timezone was valid
*/
public static function isValidTimezone($timezone) {
// If the $timezone param is an instance of the DateTimeZone class, parse the value
if($timezone instanceof \DateTimeZone)
$timezone = $timezone->getName();
// Check if the timezone is valid, and return the result
return (in_array($timezone, timezone_identifiers_list()));
}
/**
* Get a list of valid timezones
* @param bool $timezone_disp_name Should the timezone display name be included
* @param bool $timezone_offset Should the timezone offset in seconds and hours be included (heavy)
* @return array Array of timezones
*/
public static function getTimezones($timezone_disp_name = true, $timezone_offset = false) {
// Define the variable to put all the timezones in
$zones = array();
// Put each timezone identifier into the array
foreach(timezone_identifiers_list() as $i => $timezone) {
// Define the item that needs to be pushed in the $zones array
$item = array('name' => $timezone);
// Should the timezone display name be put into the array
if($timezone_disp_name)
$item['display_name'] = str_replace('/', ' - ', str_replace('_', ' ', $timezone));
// Should the timezone offset be put into the array
if($timezone_offset) {
$item['offset'] = self::getTimezoneOffset($timezone);
$item['offset_hours'] = ($item['offset'] / 60 / 60);
}
// Push the item into the $zones array
array_push($zones, $item);
}
// Return the array with the time zones
return $zones;
}
/**
* Get the current date
* @param string $format Date format
* @param null $timezone The timezone of the date, null to use the default timezone (default: null)
* @return string Date
*/
public static function getDate($format = DEFAULT_DATE_FORMAT, $timezone = null) {
// Parse the $timezone param value
$timezone = trim($timezone);
// Store the last timezone
$last_timezone = null;
// Check if a custom timezone should be used
if($timezone != null) {
// Check if the time zone is different than the current one
if(date_default_timezone_get() != $timezone) {
// Store the current timezone
$last_timezone = date_default_timezone_get();
// Set the current timezone
date_default_timezone_set($timezone);
}
}
// TODO: Get the date to return here...
$date = date($format);
// Reset the timezone to the original value
if($last_timezone != null)
date_default_timezone_set($last_timezone);
// Return the date
return $date;
}
/**
* Get the GMT date
* @param string $format PHP Date format
* @return string GMT date
*/
public static function getGmtDate($format = DEFAULT_DATE_FORMAT) {
return gmdate($format);
}
/**
* Get the GMT date with a specified offset in seconds
* @param string $format PHP Date format (optional)
* @param int $gmt_offset Offset in seconds (optional)
* @return string Date string in GMT with the offset in seconds applied
*/
public static function getGmtDateWithOffset($format = DEFAULT_DATE_FORMAT, $gmt_offset = 0) {
return gmdate($format, time() + $gmt_offset);
}
// TODO: Make configurable in the registry database
/**
* Get the default date format
* @return string Default date format
*/
public static function getDefaultDateFormat() {
return DEFAULT_DATE_FORMAT;
}
// TODO: Better desc for the $noDST param
/**
* Get the offset in seconds from the GMT time in a specific timezone
* @param string|\DateTimeZone $timezone The timezone
* @param string $date The date to get the offset on
* @param bool $addDST True to add one hour when daylight saving time is active
* @return int Timezone offset in seconds
*/
public static function getTimezoneOffset($timezone = null, $date = 'now', $addDST = false) {
// Make sure the $timezone param is not null
if($timezone == null)
$timezone = date_default_timezone_get();
// If the $timezone param is an instance of the DateTimeZone class, parse the value
if($timezone instanceof \DateTimeZone)
$timezone = $timezone->getName();
// Define the DateTime object to get the offset from
$dt = new \DateTime($date);
// Should the timezone be set
if($timezone != null) {
// Check if the value has to be converted to a DateTimeZone object
if($timezone instanceof \DateTimeZone)
$dt->setTimezone($timezone);
else
$dt->setTimezone(new \DateTimeZone($timezone));
}
// Return the offset
if(!$addDST && DateUtils::isTimezoneInDST($timezone))
return ($dt->getOffset() - (60*60));
else
return $dt->getOffset();
}
// TODO: Better desc for the $noDST param
/**
* Get the offset in hours from the GMT time in a specific timezone
* @param string|\DateTimeZone $timezone The timezone
* @param string $date The date to get the offset on
* @param bool $addDST True to add one hour when daylight saving time is active
* @return int Timezone offset in hours
*/
public static function getTimezoneOffsetHours($timezone = null, $date = 'now', $addDST = false) {
return (self::getTimezoneOffset($timezone, $date, $addDST) / 60 / 60);
}
/**
* Check if a timezone is in daylight saving time at a specified moment
* @param string|\DateTimeZone $timezone The timezone
* @param string $date The specified time
* @return bool True if in daylight saving time
*/
public static function isTimezoneInDST($timezone, $date = 'now') {
// If the $timezone param is an instanceof the DateTimeZone class, parse the value
if($timezone instanceof \DateTimeZone)
$timezone = $timezone->getName();
// Define the DateTime object to get the offset from
$dt = new \DateTime($date);
// Should the timezone be set
if($timezone != null) {
// Check if the value has to be converted to a DateTimeZone object
if($timezone instanceof \DateTimeZone)
$dt->setTimezone($timezone);
else
$dt->setTimezone(new \DateTimeZone($timezone));
}
// Return if this timezone is currently in DST
return ($dt->format('I') == '1');
}
/**
* Get the DateTime object in GMT time
* @param string $date The date (optional)
* @return \DateTime DateTime object in GMT time
*/
public static function getGmtDateTime($date = 'now') {
return new \DateTime($date, new \DateTimeZone('UCT'));
}
// TODO: Remove those lines bellow?
/**
* Convert a date to the GMT timezone
* @param string $input_date PHP Date string
* @param double $input_gmt_offset GMT offset of the input date in hours (optional)
* @param string $output_format PHP Date format for the output date (optional)
* @return string GMT date
*/
public static function toGmtDate($input_date, $input_gmt_offset = 0, $output_format = "Y-m-d H:i:statements") {
return self::toDate($input_date, $input_gmt_offset, $output_format = 0, $output_format);
}
/**
* Convert a date to a different time zone
* @param $input_date String PHP Date string
* @param $input_gmt_offset Double GMT offset of the input date in hours (optional)
* @param $output_gmt_offset Double GMT offset for the output date in hours (optional)
* @param $output_format String PHP Date format for the output date (optional)
* @return string Date with different offset
*/
public static function toDate($input_date, $input_gmt_offset = 0, $output_gmt_offset = 0, $output_format = "Y-m-d H:i:statements") {
return gmdate(
$output_format,
strtotime($input_date) - ($input_gmt_offset * 60 * 60) + ($output_gmt_offset * 60 * 60)
);
}
}
<?php
/**
* IpUtils.php
* IpUtils class for Carbon CMS.
* Ip utilities class.
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2013, All rights reserved.
*/
namespace carbon\core\util;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* IpUtils class
* @package core\util
* @author Tim Visee
*/
class IpUtils {
/**
* Get the IP address of the client. If the IP address could not be retrieved, 0.0.0.0 will be returned.
* @return string Retrieved IP address
*/
public static function getClientIp() {
// Try to retrieve the IP address of the client from some $_SERVER variables
foreach(array("REMOTE_ADDR", "HTTP_X_FORWARDED_FOR", "HTTP_CLIENT_IP") as $key)
if(isset($_SERVER[$key]))
return $_SERVER[$key];
// Unknown IP address, a proxy might have been used, returns 0.0.0.0 as default IP address
return '0.0.0.0';
}
/*public static function getClientIp($proxy = false) {
if ($proxy === true) {
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED') as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (array_map('trim', explode(',', $_SERVER[$key])) as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip;
}
}
}
}
}
if(isset($_SERVER["REMOTE_ADDR"]))
return $_SERVER["REMOTE_ADDR"];
else if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
return $_SERVER["HTTP_X_FORWARDED_FOR"];
else if(isset($_SERVER["HTTP_CLIENT_IP"]))
return $_SERVER["HTTP_CLIENT_IP"];
// Unknown IP address, a proxy might have been used, returns 0.0.0.0 as default IP address
return '0.0.0.0';
}*/
/**
* Checks whether the IP address of the client is unknown or not
* @return bool True if the IP address of the client is unknown, because a proxy might be used
*/
public static function isClientIpUnknown() {
return (IpUtils::getClientIp() == '0.0.0.0');
}
}
<?php
/**
* StringUtils.php
* StringUtils class for Carbon CMS.
* String utilities class.
*
* @author Tim Visee
* @website http://timvisee.com/
* @copyright Copyright © Tim Visee 2013, All rights reserved.
*/
namespace carbon\core\util;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_CORE_INIT') or die('Access denied!');
/**
* StringUtils class
*
* @package core\util
* @author Tim Visee
*/
class StringUtils {
/** @var string WHITESPACE_CHARS String containing all whitespace characters */
const WHITESPACE_CHARS = " \t\n\r\0\x0B";
/**
* Get the length of a string, UTF-8 aware
*
* @param String $str String being measured
*
* @return int The length of the string
*/
public static function len($str) {
// Try to use mbstring if available
if(function_exists("mb_strlen") && is_callable("mb_strlen"))
return mb_strlen($str);
// Decode the string before determining it'statements length
// TODO: Ensure the string is in UTF-8 format before decoding?
return strlen(utf8_decode($str));
}
/**
* Check whether two strings equal to each other. Arrays may be supplied. If any string in the first array equals
* any string in the second array, true will be returned.
*
* @param string|Array $strings The base string or an array of strings.
* @param string|Array $otherStrings The other string or an array of strings to compare the base string to.
* @param bool $matchCase [optional] True to ensure the case matches, false otherwise.
* @param bool $trim [optional] True to trim whitespaces first from the two strings.
*
* @return bool True if the two strings match, false otherwise
*/
public static function equals($strings, $otherStrings, $matchCase = true, $trim = false) {
// Convert both $strings and $otherStrings into an array
// TODO: Move these arrays to a separate method in an ArrayUtils class for faster processing!
if(!is_array($strings))
$strings = Array($strings);
if(!is_array($otherStrings))
$otherStrings = Array($otherStrings);
// Check whether the strings are equal
foreach($strings as $string) {
// Trim the string if $trim equals true
if($trim)
$string = trim($string);
// Loop through all strings in the second array
foreach($otherStrings as $otherString) {
// Trim the string if $trim equals true
if($trim)
$otherString = trim($otherString);
// Check whether the strings equal, return true if that's the case
if($matchCase ? (strcmp($string, $otherString) == 0) : (strcasecmp($string, $otherString) == 0))
return true;
}
}
// None of the strings equals, return false
return false;
}
/**
* Check whether a string contains a substring
*
* @param string $haystack Check if a string contains a sub string
* @param string|array $needles Sub string, or array with a list of sub strings.
* @param bool $caseSensitive False to check without case sensitivity
*
* @return bool True if the haystack contains the needle,
* if the $needle is a array, true will be returned if the string contains at leach one of the needles.
*/
// TODO: Handle $haystack as an array!
public static function contains($haystack, $needles, $caseSensitive = true) {
// Create an array of the needle, if it's not an array already
if(!is_array($needles))
$needles = Array($needles);
// Check for each needle, if it exists in the $haystack
$needlesCount = sizeof($needles);
for($i = 0; $i < $needlesCount; $i++) {
// Get the current needle
$needle = $needles[$i];
// Use case sensitivity or not, based on method arguments
if($caseSensitive) {
if(strpos($haystack, $needle) !== false)
return true;
} else
if(stripos($haystack, $needle) !== false)
return true;
}
// String doesn't contain this needle, return false
return false;
}
/**
* Check if a string starts with a substring
*
* @param string $haystack String to check in
* @param string $needle Sub String
* @param bool $ignoreCase Should the case be ignored
*
* @return bool True if the haystack starts with the needle
*/
public static function startsWith($haystack, $needle, $ignoreCase = false) {
// Make sure the needle length is not longer than the haystack
if(strlen($needle) > strlen($haystack))
return false;
// Compare the strings, check if it should be case sensitive
if(!$ignoreCase)
return (substr($haystack, 0, strlen($needle)) == $needle);
else
return (strtolower(substr($haystack, 0, strlen($needle))) == strtolower($needle));
}
/**
* Check if a string ends with a sub string
*
* @param string $haystack String
* @param string $needle Sub string
* @param bool $ignoreCase Should the case be ignored
*
* @return bool True if the haystack ends with the needle
*/
public static function endsWith($haystack, $needle, $ignoreCase = false) {
// Make sure the needle length is not longer than the haystack
if(strlen($needle) > strlen($haystack))
return false;
// Compare the strings, check if it should be case sensitive
if(!$ignoreCase)
return (substr($haystack, -strlen($needle)) == $needle);
else
return (strtolower(substr($haystack, -strlen($needle))) == strtolower($needle));
}
/**
* Check whether a string is using the UTF-8 charset.
*
* @param String $str String to check for
*
* @return bool True if the string is using the UTF-8 charset, false otherwise.
*/
public static function isUtf8($str) {
// Check whether the string has the proper charset. Try to use mbstring if available.
if(function_exists("mb_check_encoding") && is_callable("mb_check_encoding"))
return mb_check_encoding($str, 'UTF8');
// Check whether the string has the proper charset using regex as fallback (Inspired by pilif, Thanks!)
return (preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $str) == 1);
}
/**
* Check whether a string contains any whitespaces.
*
* @param string $str The string to check.
* @param bool $trim True to trim the string from whitespaces before running the check.
*
* @return bool True if the string contains any whitespace characters, false if the string doesn'elapsed have any
* whitespace characters or if the string was invalid.
*/
public static function containsWhitespaces($str, $trim = false) {
// Make sure $str is a string which is at least one character long
if(!is_string($str) || strlen($str) <= 0)
return false;
// Check whether we should trim the string first
if($trim)
$str = trim($str);
// Check whether the string contains any whitespaces, return the result
return (preg_match('/\s/', $str) > 0);
}
}
##########################################################################
# #
# .htaccess #
# Default .htaccess file for Carbon CMS. #
# @author Tim Visee #
# @website http://timvisee.com/ #
# @copyright Copyright (c) Tim Visee 2012-2013, All rights reserved. #
# #
##########################################################################
# Deny all requests to this directory
Deny From All
<?php
/*
* Carbon CMS configuration file
*
* IMPORTANT NOTES:
* - Any changes in this configuration file may break the site!
* - Do never share this file with others, this file contains sensitive information!
* - Do never remove this file!
*/
return Array(
// TODO: Rename site_url -> site_domain
// TODO: More 'general site' configuration options
// TODO: Add site configuration to to the Carbon CMS registery (possibly optionally!)
'general' => Array(
'site_url' => 'http://localhost/app/carbon_cms/',
'site_path' => '/app/carbon_cms'
),
'database' => Array(
'host' => '127.0.0.1',
'port' => 3306,
'database' => 'carbon_cms',
'username' => 'root',
'password' => 'password',
'table_prefix' => 'carbon_'
),
'hash' => Array(
'hash_algorithm' => 'sha256',
'hash_key' => '7cc8b7833dba4e03dd6d1441aa25262fabe29862b494aff2168f1a6b35f1f406'
),
'carbon' => Array(
'debug' => true
)
);
<?php
/**
* Controller.php
* Controller class file for Carbon CMS.
* @author Tim Visée
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012-2013, All rights reserved.
*/
namespace controller;
use model;
use \core\Database;
use \core\SessionHandler;
use \view\View;
// Prevent direct requests to this file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
/**
* Controller class, parent controller
* @package core
* @author Tim Visée
*/
class Controller {
/**
* @var Database $db Database instance
* @var Model $model The model
* @var View $view The view
*/
private $db;
private $model;
protected $view;
/**
* Constructor
* @param Database $db Database instance
*/
function __construct(Database $db) {
// Store the database instance
$this->db = $db;
// Construct the View
$this->view = new View();
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Get the model
* @return Model Model
*/
public function getModel() {
return $this->model;
}
/**
* Load a model by name
* @param string $model_name Name of the model to loadLocalesList
*/
public function loadModel($model_name) {
// Get the path the model is on
$model_path = __DIR__ . '/../model/'.strtolower($model_name).'_model.php';
// Make sure the model file exists
if(!file_exists($model_path)) {
// TODO: Show error page instead of killing the page!
die('Carbon CMS: The model \'' . $model_name . '\' does not exist!');
}
// Parse the models class name
$model_class = ucfirst(strtolower($model_name)).'_Model';
// Get the model'statements class name with namespace
$model_class_namespace = '\\model\\'.$model_class;
// Construct the model
if(strtolower($model_name) != 'page')
$this->model = new $model_class_namespace($this->getDatabase());
else
$this->model = new $model_class_namespace($this->getDatabase(), $this->getPage());
}
/**
* Get the view
* @return View View
*/
public function getView() {
return $this->view;
}
}
<?php
/**
* error.php
* Error controll file for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012, All rights reserved.
*/
namespace controller;
use controller\Controller;
use carbon\core\Database;
// Prevent direct requests to this file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class Error extends Controller {
function __construct(Database $db) {
parent::__construct($db);
}
public function index() {
$this->view->render('error/index');
}
}
<?php
/**
* login.php
* Login controller file for Carbon CMS.
* @author Tim Visée
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012-2013, All rights reserved.
*/
namespace controller;
use controller\Controller;
use carbon\core\Database;
use model\Login_Model;
// Prevent direct requests to this file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class Login extends Controller {
public function index() {
$this->view->render('login/index');
}
function run() {
$this->model->run();
}
}
<?php
/**
* page.php
* Page controller file for Carbon CMS.
* @author Tim Vis�e
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
namespace controller;
use controller\Controller;
use carbon\core\Database;
use carbon\core\Page as PageClass;
// Prevent direct requests to this file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
/**
* Page class, Controller child
* @package controller
* @author Tim Vis�e
*/
class Page extends Controller {
/**
* @var Page $page Page
*/
private $page;
/**
* Constructor
* @param Database $db Database instance
* @param Page $page Page instance
*/
public function __construct(Database $db, PageClass $page) {
// Run the Controller constructor
parent::__construct($db);
// Store the page
$this->page = $page;
}
/**
* Render the page
*/
public function render() {
// TODO: Render page header
$this->getView()->renderHeader();
// TODO: Use different way of page rendering
// Render the page body
echo $this->getModel()->getPage()->getPageBody();
// TODO: Render page footer
$this->getView()->renderFooter();
}
/**
* Get the page
* @return Page Page
*/
public function getPage() {
return $this->page;
}
}
<pre>
CARBON CORE + CMS | V0.1 PRE-ALPHA | BUILD 20932 | BY TIM VISEE
---------------------------------------------------------------
CONSOLE MODE
---------------------------------------------------------------
[Core] Loading Carbon Core and Carbon CMS...
<?php
/**
* index.php
*
* This file handles all page requests to the website in the current directory.
* This file initializes Carbon CMS and starts the bootstrap.
*
* @author Tim Visee
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright (C) Tim Visee 2012-2014. All rights reserved.
*/
// Define some constants
/** Defines the root directory for the website */
define('CARBON_SITE_ROOT', __DIR__);
// Load and initialize Carbon CMS
require(CARBON_SITE_ROOT . '/carbon/cms/init.php');
// Load, construct and initialize the Bootstrap
use carbon\cms\Bootstrap;
$bootstrap = new Bootstrap(true);
// Stop the Bootstrap
$bootstrap->shutdown();
[properties]
scope = ""
version = 0.1
[language]
Error = "Error"
Message = "Message"
Notice = "Notice"
Warning = "Warning"
[properties]
scope = ""
version = 0.1
[language]
Error = "Fout"
Message = "Bericht"
Notice = "Attentie"
Warning = "Waarschuwing"
<?php
/**
* login_model.php
* Login model class for Carbon CMS.
* @author Tim Visée
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012-2013, All rights reserved.
*/
namespace model;
use carbon\core\Database;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class Login_Model extends Model {
public function run() {
// TODO: Rewrite this bellow, table prefixes
$query = $this->db->prepared("SELECT `user_id` FROM `carbon_users` WHERE username = :username AND password = :password");
$query->execute(array(
':username' => $_POST['username'],
':password' => md5($_POST['password'])
));
$data = $query->fetchAll();
print_r($data);
}
}
<?php
/**
* Model.php
* Model class for Carbon CMS.
* @author Tim Visée
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012-2013, All rights reserved.
*/
namespace model;
use carbon\core\Database;
use carbon\core\RegistryHandler;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class Model {
/**
* @var Database $db Database instance
*/
private $db;
/**
* Constructor
* @param Database $db Database instance
*/
function __construct(Database $db) {
// Store the database instance
$this->db = $db;
}
/**
* Get the database instance
* @return Database Database instance
*/
public function getDatabase() {
return $this->db;
}
/**
* Set the database instance
* @param Database $db Database instance
*/
public function setDatabase(Database $db) {
$this->db = $db;
}
}
<?php
/**
* page_model.php
* Page Model set_file for Carbon CMS.
* @author Tim Vis�e
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012, All rights reserved.
*/
namespace model;
use carbon\core\Database;
use carbon\core\Page;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
/**
* Page_Model class, Model child class
* @package core
* @author Tim Vis�e
*/
class Page_Model extends Model {
/**
* @var Page $page Page
*/
private $page;
/**
* Constructor
* @param Database $db Database instance
* @param Page $page Page
*/
public function __construct(Database $db, Page $page) {
// Run the model'statements constructor
parent::__construct($db);
// Store the page
$this->page = $page;
// TODO: Retrieve page data and put it into an array inside the page class!
}
/**
* Get the page
* @return Page Page
*/
public function getPage() {
return $this->page;
}
}
######################################################################
# #
# .htaccess #
# Default .htaccess set_file for Carbon CMS. #
# @author Tim Vis�e #
# @website http://timvisee.com/ #
# @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved. #
# #
######################################################################
# Use PHP 5.3
<FilesMatch "\.php$">
AddHandler x-httpd-php53 .php
</FilesMatch>
deny from all
<?php
/**
* Calendar.php
* Main set_file of the Calendar plugin for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
// namespace plugins\calentar;
use carbon\core\event\EventCallEvent;
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class Calendar extends Module {
public function onLoad() {
// echo $this->getModuledispName() . ' loaded! :D<br />';
}
public function onEnable() {
// Include the event listener class
include(__DIR__ . '/EventListener.php');
// Setup the event listener
$eventListener = new EventListener($this);
// Get the event manager and register the event listeners
$event_manager = $this->getPluginManager()->getEventManager();
$event_manager->registerEvents($eventListener, $this);
// echo 'Calendar plugin enabled!<br />';
}
public function onDisable() {
// echo $this->getModuledispName() . ' disabled! :(<br />'
}
}
<?php
/**
* EventListener.php
* Basig event listener for the Calander plugin.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
use carbon\core\event\Listener;
use carbon\core\event\plugin\PluginEnableEvent;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class EventListener extends Listener {
private $plugin = null;
public function __construct(Calendar $instance) {
$this->pugin = $instance;
}
public function getPlugin() {
return $this->plugin;
}
public function onPluginEnable(PluginEnableEvent $event) {
//echo '<br />Module enabled: ' . $event->getPlugin()->getModuledispName() . '<br />';
//$event->setCaceled(true);
//echo '<br />Module enable canceled: ' . $event->getPlugin()->getModuledispName() . '<br />';
}
}
; Module settings file
[plugin]
main = Calendar.php
main_class = Calendar
name = calendar
display_name = Calendar
description = Carbon CMS Calendar plugin
version = 0.1
website = http://timvisee.com/
[author]
name = Tim Visee
website = http://timvisee.com/
; Module settings file
[plugin]
main = Module.php
main_class = TestClass
name = plugin1
display_name = Module 1
description = Test Module
version = 0.1
website = http://timvisee.com/
[author]
name = Tim Visee
website = http://timvisee.com/
<?php
/**
* Module.php
* Test Module for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class TestClass extends Module {
public function onLoad() {
// echo $this->getModuledispName() . ' loaded! :D<br />';
}
public function onEnable() {
// echo $this->getModuledispName() . ' enabled! :D<br />';
}
public function onDisable() {
// echo $this->getModuledispName() . ' disabled! :(<br />';
}
}
<?php
/**
* Main.php
* Main set_file of test plugin for Carbon CMS.
* @author Tim Vis�e
* @website http://timvisee.com/
* @copyright Copyright � Tim Vis�e 2012-2013, All rights reserved.
*/
use carbon\core\module\Module;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
class MainClass extends Module {
public function onLoad() {
// echo $this->getModuledispName() . ' loaded! :D<br />';
}
public function onEnable() {
// echo $this->getModuledispName() . ' enabled! :D<br />';
}
public function onDisable() {
// echo $this->getModuledispName() . ' disabled! :(<br />';
}
}
; Module settings file
[plugin]
name = plugin2
display_name = Module 2
main = Main.php
main_class = MainClass
description = Test Module 2
version = 0.1
website = http://timvisee.com/
[author]
name = Tim Visee
website = http://timvisee.com/
<?php
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
?>
Error Page!
<?php
/**
* header.php
* Main header set_file for views for Carbon CMS.
* @author Tim Visée
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012-2013, All rights reserved.
*/
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
use carbon\core\Core;
?>
<!DOCTYPE HTML>
<html>
<head>
<title>Carbon CMS v<?=Core::getVersionName(); ?></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link href="/carbon_cms/theme/default/style/style.css" rel="stylesheet" type="text/css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<?php
if(isset($this->js)) {
$site_url = $GLOBALS['carbon_config']->getValue('general', 'site_url');
foreach($this->js as $js)
echo '<script type="text/javascript" src="'.$site_url.'view/'.$js.'"></script>';
}
?>
</head>
<body>
<div id="menubar">
<a href="/app/carbon_cms/">Index</a> <a href="help">Help</a> <a href="login">Login</a> <a href="dashboard">Dashboard</a> <a href="error">Error</a><br />
</div>
<div id="page">
<?php
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
?>
<h1>Login</h1>
<form action="" method="POST">
<label>Username:</label> <input type="text" name="username" /><br />
<label>Password:</label> <input type="password" name="password" /><br />
<label></label><input type="submit" />
</form>
<?php
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
?>
Normal page!
<?php
/**
* View.php
* View set_file for Carbon CMS.
* @author Tim Visée
* @version 0.1
* @website http://timvisee.com/
* @copyright Copyright © Tim Visée 2012, All rights reserved.
*/
namespace view;
// Prevent direct requests to this set_file due to security reasons
defined('CARBON_ROOT') or die('Access denied!');
/**
* View class
* @package view
* @author Tim Visée
*/
class View {
/**
* Constructor
*/
function __construct() { }
/**
* Render the page header
*/
public function renderHeader() {
require(__DIR__ . '/../view/header.php');
}
/**
* Render the page footer
*/
public function renderFooter() {
require(__DIR__ . '/../view/footer.php');
}
/**
* Render the page
* @param string $name View name
*/
public function render($name) {
// Get the model path
$view_path = __DIR__ . '/../view/'.$name.'.php';
// Check if the view exists
if(!file_exists($view_path)) {
// TODO: Show error page instead of killing the script with an error
die('Carbon CMS: The model \'' . $view_path . '\' does not exist!');
}
// Render the body
require(__DIR__ . '/../view/'.$name.'.php');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment