Compare commits

..

No commits in common. "master" and "1.0" have entirely different histories.
master ... 1.0

81 changed files with 17809 additions and 4918 deletions

View file

@ -1 +0,0 @@
service_name: travis-ci

3
.gitignore vendored
View file

@ -5,10 +5,7 @@
##########################
.*
!.gitignore
!.travis.yml
!.coveralls.yml
*~
vendor
# Global/OSX.gitignore #
########################

View file

@ -1,39 +0,0 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- hhvm
matrix:
allow_failures:
- php: hhvm
branches:
only:
- master
- 2.0
- 1.9
services:
- memcache
- memcached
- redis
install:
- composer self-update
- composer install
before_script:
- ./tests/travis.sh
- mysql -e "create database IF NOT EXISTS test;" -u root
- mysql test < tests/schema.sql -u root
- mkdir -p build/logs
- phpenv rehash
script:
- phpunit --coverage-clover /home/travis/build/joshtronic/pickles/build/logs/clover.xml
after_success:
- php vendor/bin/coveralls --config ../.coveralls.yml -v

20
LICENSE
View file

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2007-2014 Josh Sherman
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
MIT-LICENSE.txt Normal file
View file

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2011 Josh Sherman, https://github.com/joshtronic/pickles
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,28 +1,48 @@
# Pickles
# PHP Interface Collection of Killer Libraries to Enhance Stuff
[![License](http://img.shields.io/packagist/l/joshtronic/pickles.svg?style=flat)][packagist]
[![Build](http://img.shields.io/travis/joshtronic/pickles.svg?style=flat)][travis]
[![Coverage](http://img.shields.io/coveralls/joshtronic/pickles.svg?style=flat)][coveralls]
[![Downloads](http://img.shields.io/packagist/dt/joshtronic/pickles.svg?style=flat)][packagist]
## What is PICKLES?
Pickles f/k/a PICKLES (PHP Interface Collection of Killer Libraries to Enhance
Stuff) is an open source framework for the rapid development of RESTful
services. The intention of this framework is to allow developers to a means to
develop service-oriented backend systems that are completely decoupled from the
front end components. Thus allowing the freedom to build the front end
implementation(s) using whichever tools they choose, be it Ember.js, Angular.js
or some cool new system Ive yet to hear of.
PICKLES is an open source framework for the rapid development of web applications.
## Thanks
## Okay, but why?
Special thanks to [Geoff Oliver][GeoffOliver] for being a long time user and
contributor, [Justin Davis][JustinDavis] for romanticizing the v2 reimagining
and [Dean Jones][DeanJones] for helping to come up with the original PICKLES v1
acronym.
I could have went with any number of existing frameworks, but I opted to build my own because I like to code. I've built quite a few sites from the ground up over the years, and I wanted to roll that experience into a single system that I could not only use for my sites, but share with the world.
[coveralls]: https://coveralls.io/r/joshtronic/pickles
[packagist]: https://packagist.org/packages/joshtronic/pickles
[travis]: http://travis-ci.org/joshtronic/pickles
[DeanJones]: https://github.com/deanproxy
[GeoffOliver]: https://github.com/geoffoliver
[JustinDavis]: http://justindavis.co
## Wait, what, it's not MVC?
PICKLES is in fact not a true MVC system and won't be masquerading around as one (yeah, I know, I borrowed some naming conventions). PICKLES does have a main controller that handles incoming page views. The controller loads a module that contains all of the business logic (optionally interacting with data models) and then execution is passed off to the display layer. The display layer gives the user what they asked for (even if they didn't say please). This is how web pages work, and there has never been a reason for me to force PICKLES into the MVC box just for the hell of it.
## Requirements
### Required Software
* Web server (nginx or Apache with mod_rewrite)
* PHP 5.3+
Please note that PICKLES can run on 5.0+ (you may need the PECL JSON library for versions below 5.2) but is heavily field tested on servers running the latest LTS release of Ubuntu (12.04 at the moment) running the latest stable version of PHP in the main repository (which is the 5.3.x branch). nginx is the preferred web server at this point, but PICKLES was deployed to and used on Apache 1.3+ for quite sometime.
### Highly Recommended Software
* PHP 5.2.0+ for native JSON support or PECL JSON 1.2.1
### Optional Software
#### Databases
* MySQL with PDO and PDO_MYSQL drivers
* PostgreSQL with PDO and PDO_PGSQL drivers
* SQLite 3 with PDO and PDO_SQLITE drivers
#### CSS Pre-processors
* node, npm & lessc to compile LESS files
* sass to compile SASS files
## Installation
Installation is quite simple as there is no installer to run, and all server configuration options can be set in your index.php for your site.
1. Download the source http://github.com/joshtronic/pickles/zipball/master (or clone the repository)
2. Place the code anywhere you'd like (that's at least 2 directories up from the root of your website). I recommend using /usr/share/pickles
3. A starter site can be obtained from http://github.com/joshtronic/pickles-starter. It has everything you need to get a site up and running.
4. At this point you should have a very rudimentary site up and running.

43
classes/API/Common.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/**
* Common API Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Common API Interface
*
* Parent class that our API interface classes should be extending. Contains
* execution of parental functions but may contain more down the road.
*/
abstract class API_Common extends Object
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
}
/**
* Destructor
*/
public function __destruct()
{
parent::__destruct();
}
}
?>

View file

@ -0,0 +1,47 @@
<?php
/**
* Google Profanity Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Google Profanity API Interface
*/
class API_Google_Profanity extends API_Common
{
/**
* Check
*
* Checks if a word is considered profanity.
*
* @usage API_Google_Profanity::check('fuck'); // returns true
* @param string $word word to check
* @return boolean whether or not the word is profanity
*/
public static function check($word)
{
$response = json_decode(file_get_contents('http://www.wdyl.com/profanity?q=' . $word), true);
if ($response == null || !isset($response['response']) || !in_array($response['response'], array('true', 'false')))
{
throw new Exception('Invalid response from API.');
}
else
{
return $response['response'] == 'true';
}
}
}
?>

42
classes/API/Gravatar.php Normal file
View file

@ -0,0 +1,42 @@
<?php
/**
* Gravatar Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Gravatar API Interface
*
* @link http://en.gravatar.com/site/implement/
*/
class API_Gravatar extends API_Common
{
/**
* Hash
*
* Generates a hash from the passed string that can then be used for
* fetching an image or profile from Gravatar.com
*
* @static
* @param string $string string to hash, should be an email address
* @return string resulting hash
*/
public static function hash($string)
{
// Trims whitespace, lowers the case then applies MD5
return md5(strtolower(trim($string)));
}
}
?>

185
classes/API/Tinychat.php Normal file
View file

@ -0,0 +1,185 @@
<?php
/**
* Tinychat Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Tinychat API Interface
*
* @link http://tinychat.com/developer/docs
*/
class API_Tinychat extends API_Common
{
/**
* Public Key
*
* @access private
* @var string
*/
private $public_key = null;
/**
* Secret Key
*
* @access private
* @var string
*/
private $secret_key = null;
/**
* Constructor
*
* Assigns our public and secret keys from the configuration.
*/
public function __construct()
{
parent::__construct();
if (isset($this->config->api['tinychat'], $this->config->api['tinychat']['public_key'], $this->config->api['tinychat']['secret_key']))
{
$this->public_key = $this->config->api['tinychat']['public_key'];
$this->secret_key = $this->config->api['tinychat']['secret_key'];
}
else
{
throw new Exception('Unable to load TinyChat configuration.');
}
}
/**
* Execute
*
* Constructs a valid API call, executes it and returns the results.
*
* @param string $codephrase name of the API call being called
* @param string $authentication post-codephrase portion of the auth string
* @param array $parameters key / value pairs for additional data
* @return array results of the API call
*/
private function execute($codephrase, $authentication, $parameters = null)
{
// Assembles and hashes the authentication token
$authentication = md5($this->secret_key . ':' . $authentication);
// Assembles any additional parameters
$additional = '';
if ($parameters && is_array($parameters))
{
foreach ($parameters as $key => $value)
{
$additional .= '&' . $key . '=' . $value;
}
}
// Executes the API call
$results = file_get_contents('http://tinychat.apigee.com/' . $codephrase . '?result=json&key=' . $this->public_key . '&auth=' . $authentication . $additional);
return json_decode($results, true);
}
/**
* List Rooms
*
* Pulls all rooms for the API application.
*
* @return array API results
*/
public function listRooms()
{
return $this->execute('roomlist', 'roomlist');
}
/**
* Room Info
*
* Pulls the information for a room.
*
* @param string $room name of the room
* @param boolean $with_ip whether or not to include users IP addresses
* @return array API results
*/
public function roomInfo($room, $with_ip = false)
{
return $this->execute('roominfo', $room . ':roominfo', array('room' => $room, 'with_ip' => ($with_ip ? 1 : 0)));
}
/**
* Set Room Password
*
* Sets the password for the room, only users with the correct password
* will be able to enter.
*
* @param string $room name of the room
* @param string $password password to use, blank for no password
* @return array API results
*/
public function setRoomPassword($room, $password = '')
{
return $this->execute('setroompassword', $room . ':setroompassword', array('room' => $room, 'password' => $password));
}
/**
* Set Broadcast Password
*
* Sets the password to allow broadcasting in the room. Only users with the
* correct password will be able to broadcast.
*
* @param string $room name of the room
* @param string $password password to use, blank for no password
* @return array API results
*/
public function setBroadcastPassword($room, $password = '')
{
return $this->execute('setbroadcastpassword', $room . ':setbroadcastpassword', array('room' => $room, 'password' => $password));
}
/**
* Generate HTML
*
* Creates the HTML to place a chat on a site.
*
* @todo List params...
* @return array API results
*/
public function generateHTML($room, $join = false, $nick = false, $change = false, $login = false, $oper = false, $owner = false, $bcast = false, $api = false, $colorbk = false, $tcdisplay = false, $autoop = false, $urlsuper = false, $langdefault = false)
{
return '
<script type="text/javascript">
var tinychat = {'
. 'room: "' . $room . '",'
. ($join ? 'join: "auto",' : '')
. ($nick ? 'nick: "' . $nick . '",' : '')
. ($change ? 'change: "none",' : '')
. ($login ? 'login: "' . $login . '",' : '')
. ($oper ? 'oper: "none",' : '')
. ($owner ? 'owner: "none",' : '')
. ($bcast ? 'bcast: "restrict",' : '')
. ($api ? 'api: "' . $api . '",' : '')
. ($colorbk ? 'colorbk: "' . $colorbk . '",' : '')
. ($tcdisplay ? 'tcdisplay: "vidonly",' : '')
/* @todo Implement $autoop, it's an array and needs validated */
. ($urlsuper ? 'urlsuper: "' . $urlsuper . '",' : '')
. ($langdefault ? 'langdefault: "' . $langdefault . '",' : '')
. 'key: "' . $this->public_key . '"'
. '};
</script>
<script src="http://tinychat.com/js/embed.js"></script>
<div id="client"></div>
';
}
}
?>

277
classes/Browser.php Normal file
View file

@ -0,0 +1,277 @@
<?php
/**
* Browser Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Browser Utility Class
*
* Just a simple collection of static functions to accomplish some of the
* more redundant browser-related tasks.
*/
class Browser extends Object
{
/**
* Attributes
*
* Variables passed on the query string as /var:value/
*
* @access private
* @var array
*/
private $attributes = array();
/**
* Get instance of the object
*
* Let's the parent class do all the work
*
* @static
* @param string $class name of the class to instantiate
* @return object self::$instance instance of the Config class
*/
public static function getInstance($class = 'Browser')
{
return parent::getInstance($class);
}
/**
* Set browser variable
*
* Sets a variable in the attributes array for easier access later.
*
* @static
* @param string $variable name of the variable to set
* @param mixed $value the value to set to the variable
* @return boolean true
*/
public static function set($variable, $value)
{
$browser = Browser::getInstance();
$browser->attributes[$variable] = $value;
return true;
}
/**
* Get browser variable
*
* Gets a variable passed in from the browser. Currently only supports
* the custom attribute URI format /$variable:$value/.
*
* @static
* @param string $variable name of the variable to get
* @return mixed the value of the variable or boolean false if not set
*/
public static function get($variable)
{
$browser = Browser::getInstance();
if (isset($browser->attributes[$variable]))
{
return $browser->attributes[$variable];
}
return false;
}
/**
* Go Home
*
* Alias for `Browser::redirect('/');`
*
* @static
*/
public static function goHome()
{
Browser::redirect('/');
}
/**
* Is Mobile
*
* Detects if we're working with a mobile browser.
*
* @return boolean whether or not the browser is considered mobile
*/
public static function isMobile()
{
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
return preg_match('/android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $user_agent) || preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i', substr($user_agent, 0, 4)) || strpos('iPhone', $user_agent);
}
/**
* Redirect
*
* Redirects the browser to another URL. Stops execution as to not run
* code erroneously due to output buffering. HTTP/1.1 request an
* absolute URI, hence the inclusion of the scheme, hostname and
* absolute path if :// is not found. Don't hate the player, hate the
* RFC.
*
* @static
* @param string $destination URL to redirect to
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30
*/
public static function redirect($destination)
{
if (strpos($destination, '://') === false)
{
$destination = 'http' . ((!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off' || $_SERVER['HTTPS'] == '') ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . $destination;
}
header('Location: ' . $destination);
exit;
}
/**
* Remote IP
*
* Returns the user's IP address.
*
* @return mixed IP address or false if unable to determine
*/
public static function remoteIP()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip = $_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
elseif (isset($_SERVER['REMOTE_ADDR']))
{
$ip = $_SERVER['REMOTE_ADDR'];
}
else
{
$ip = false;
}
return $ip;
}
/**
* Status
*
* Pushes a status code to the browser. Some of these codes are site
* (420) and server (444) specific, some just for LOLs (418) and some
* that are still in draft (425) and subject to change. I wanted this
* to be a complete list and in the scenario that a code had multiple
* meanings, I favored a more recent RFC (424) even if merely a draft
* (451).
*
* @static
* @param integer status response code
*/
public static function status($code = 200)
{
switch ($code)
{
// {{{ 1xx Informational
case 100: $message = '100 Continue'; break;
case 101: $message = '101 Switching Protocols'; break;
case 102: $message = '102 Processing'; break;
// }}}
// {{{ 2xx Success
case 200: $message = '200 OK'; break;
case 201: $message = '201 Created'; break;
case 202: $message = '202 Accepted'; break;
case 203: $message = '203 Non-Authoritative Information'; break;
case 204: $message = '204 No Content'; break;
case 205: $message = '205 Reset Content'; break;
case 206: $message = '206 Partial Content'; break;
case 207: $message = '207 Multi-Status'; break;
case 208: $message = '208 Already Reported'; break;
case 226: $message = '226 IM Used'; break;
// }}}
// {{{ 3xx Redirection
case 300: $message = '300 Multiple Choices'; break;
case 301: $message = '301 Moved Permanently'; break;
case 302: $message = '302 Found'; break;
case 303: $message = '303 See Other'; break;
case 304: $message = '304 Not Modified'; break;
case 305: $message = '305 Use Proxy'; break;
case 306: $message = '306 Switch Proxy'; break;
case 307: $message = '307 Temporary Redirect'; break;
case 308: $message = '308 Permanent Redirect'; break;
// }}}
// {{{ 4xx Client Error
case 400: $message = '400 Bad Request'; break;
case 401: $message = '401 Unauthorized'; break;
case 402: $message = '402 Payment Required'; break;
case 403: $message = '403 Forbidden'; break;
case 404: $message = '404 Not Found'; break;
case 405: $message = '405 Method Not Allowed'; break;
case 406: $message = '406 Not Acceptable'; break;
case 407: $message = '407 Proxy Authentication Required'; break;
case 408: $message = '408 Request Timeout'; break;
case 409: $message = '409 Conflict'; break;
case 410: $message = '410 Gone'; break;
case 411: $message = '411 Length Required'; break;
case 412: $message = '412 Precondition Failed'; break;
case 413: $message = '413 Request Entity Too Large'; break;
case 414: $message = '414 Request-URI Too Long'; break;
case 415: $message = '415 Unsupported Media Type'; break;
case 416: $message = '416 Requested Range Not Satisfied'; break;
case 417: $message = '417 Expectation Failed'; break;
case 418: $message = '418 I\'m a teapot'; break;
case 420: $message = '420 Enhance Your Calm'; break;
case 422: $message = '422 Unprocessed Entity'; break;
case 423: $message = '423 Locked'; break;
case 424: $message = '424 Failed Dependency'; break;
case 425: $message = '425 Unordered Collection'; break;
case 426: $message = '426 Upgrade Required'; break;
case 428: $message = '428 Precondition Required'; break;
case 429: $message = '429 Too Many Requests'; break;
case 431: $message = '431 Request Header Fields Too Large'; break;
case 444: $message = '444 No Response'; break;
case 449: $message = '449 Retry With'; break;
case 450: $message = '450 Blocked by Windows Parental Controls'; break;
case 451: $message = '451 Unavailable for Legal Reasons'; break;
case 494: $message = '494 Request Header Too Large'; break;
case 495: $message = '495 Cert Error'; break;
case 496: $message = '496 No Cert'; break;
case 497: $message = '497 HTTP to HTTPS'; break;
case 499: $message = '499 Client Closed Request'; break;
// }}}
// {{{ 5xx Server Error
case 500: $message = '500 Internal Server Error'; break;
case 501: $message = '501 Not Implemented'; break;
case 502: $message = '502 Bad Gateway'; break;
case 503: $message = '503 Service Unavailable'; break;
case 504: $message = '504 Gateway Timeout'; break;
case 505: $message = '505 HTTP Version Not Supported'; break;
case 506: $message = '506 Variant Also Negotiates'; break;
case 507: $message = '507 Insufficient Storage'; break;
case 508: $message = '508 Loop Detected'; break;
case 509: $message = '509 Bandwidth Limit Exceeded'; break;
case 510: $message = '510 Not Extended'; break;
case 511: $message = '511 Network Authentication Required'; break;
case 598: $message = '598 Network read timeout error'; break;
case 599: $message = '599 Network connect timeout error'; break;
// }}}
default: $message = '200 OK'; break;
}
header('HTTP/1.1 ' . $message, true, $code);
header('Status: ' . $message, true, $code);
}
}
?>

280
classes/Cache.php Normal file
View file

@ -0,0 +1,280 @@
<?php
/**
* Caching System for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Cache Class
*
* Wrapper class for Memcache() to allow for better error handling when the
* Memcached server is unavailable. Designed around the syntax for Memcached()
* to allow for an easier transistion to the aforementioned in the future. I
* don't entirely remember specifics, but the reason for not using Memcached()
* was due to an unexplainable bug in the version in the repository for Ubuntu
* 10.04 LTS. Memcached() does support more of the memcached protocol and will
* eventually be what PICKLES uses. Keys are forced to be uppercase for
* consistencies sake as I've been burned by the case sensitivity due to typos
* in my code.
*
* Requires php5-memcache
*
* @link http://us.php.net/manual/en/book.memcache.php
* @link http://packages.ubuntu.com/lucid/php5-memcache
* @link http://www.memcached.org/
*/
class Cache extends Object
{
/**
* Namespace (prefix)
*
* @access private
* @var string
*/
private $namespace = '';
/**
* Servers
*
* @access private
* @var integer
*/
private $servers = 0;
/**
* Connection resource to Memcached
*
* @access private
* @var object
*/
private $connection = null;
/**
* Constructor
*
* Sets up our connection variables.
*
* @param string $hostname optional hostname to connect to
* @param string $database optional port to use
*/
public function __construct()
{
parent::__construct();
if ($this->config->pickles['cache'])
{
if (!is_array($this->config->pickles['cache']))
{
$datasources = array($this->config->pickles['cache']);
}
else
{
$datasources = $this->config->pickles['cache'];
}
$this->connection = new Memcache();
foreach ($datasources as $name)
{
if (isset($this->config->datasources[$name]))
{
$datasource = $this->config->datasources[$name];
$this->connection->addServer($datasource['hostname'], $datasource['port']);
$this->servers++;
if (isset($datasource['namespace']))
{
$this->namespace = $datasource['namespace'];
}
}
}
}
if ($this->namespace != '')
{
$this->namespace .= '-';
}
}
/**
* Destructor
*
* Closes the connection when the object dies.
*/
public function __destruct()
{
if ($this->servers)
{
$this->connection->close();
}
}
/**
* Get Instance
*
* Let's the parent class do all the work.
*
* @static
* @param string $class name of the class to instantiate
* @return object self::$instance instance of the Cache class
*/
public static function getInstance($class = 'Cache')
{
return parent::getInstance($class);
}
/**
* Get Key
*
* Gets the value of the key(s) and returns it.
*
* @param mixed $keys key(s) to retrieve
* @return mixed value(s) of the requested key(s), false if not set
*/
public function get($keys)
{
set_error_handler('cacheErrorHandler');
if (is_array($keys))
{
foreach ($keys as $index => $key)
{
$keys[$index] = strtoupper($this->namespace . $key);
}
}
else
{
$keys = strtoupper($this->namespace . $keys);
}
try
{
$return = $this->connection->get($keys);
}
catch (Exception $exception)
{
$return = false;
}
restore_error_handler();
return $return;
}
/**
* Set Key
*
* Sets key to the specified value. I've found that compression can lead to
* issues with integers and can slow down the storage and retrieval of data
* (defeats the purpose of caching if you ask me) and isn't supported. I've
* also been burned by data inadvertantly being cached for infinity, hence
* the 5 minute default.
*
* @param string $key key to set
* @param mixed $value value to set
* @param integer $expiration optional expiration, defaults to 5 minutes
* @return boolean status of writing the data to the key
*/
public function set($key, $value, $expire = 300)
{
set_error_handler('cacheErrorHandler');
$key = strtoupper($key);
try
{
$return = $this->connection->set(strtoupper($this->namespace . $key), $value, 0, $expire);
}
catch (Exception $exception)
{
$return = false;
}
restore_error_handler();
return $return;
}
/**
* Delete Key
*
* Deletes the specified key(s).
*
* @param mixed $keys key(s) to delete
* @return boolean status of deleting the key
*/
public function delete($keys)
{
set_error_handler('cacheErrorHandler');
try
{
if (!is_array($keys))
{
$keys = array($keys);
}
// Memcache() doesn't let you pass an array to delete all records the same way you can with get()
foreach ($keys as $key)
{
$this->connection->delete(strtoupper($this->namespace . $key));
}
$return = true;
}
catch (Exception $exception)
{
$return = false;
}
restore_error_handler();
return $return;
}
/**
* Increment Key
*
* Increments the value of an existing key.
*
* @param string $key key to increment
* @return boolean status of incrementing the key
* @todo Check if it's set as Memcache() doesn't and won't inc if it doesn't exist
*/
public function increment($key)
{
set_error_handler('cacheErrorHandler');
try
{
$return = $this->connection->increment(strtoupper($this->namespace . $key));
}
catch (Exception $exception)
{
$return = false;
}
restore_error_handler();
return $return;
}
}
function cacheErrorHandler($errno, $errstr, $errfile, $errline)
{
throw new Exception($errstr);
}
?>

309
classes/Config.php Normal file
View file

@ -0,0 +1,309 @@
<?php
/**
* Configuration Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Config Class
*
* Handles loading the site's configuration file (if available). At the moment
* this class is a very skewed Singleton. The plan is to eventually extend this
* out to support multiple configuration files, and the ability to load in
* custom config files on the fly as well. The core of PICKLES uses the class
* as a Singleton so we're not loading the configuration multiple times per
* page load.
*
* @usage <code>$config = new Config($filename);</code>
*/
class Config extends Object
{
/**
* Config data
*
* @access private
* @var array
*/
private $data = array();
/**
* Constructor
*
* Calls the parent constructor and loads the passed file.
*
* @param string $filename optional Filename of the config
*/
public function __construct($filename = null)
{
parent::__construct();
// Try to fine the configuration
if ($filename == null)
{
$filename = 'config.php';
$loaded = false;
$cwd = getcwd();
while ($loaded == false)
{
chdir(dirname($filename));
if (getcwd() == '/')
{
throw new Exception('Unable to load configuration.');
}
chdir($cwd);
$filename = '../' . $filename;
$loaded = $this->load($filename);
}
}
else
{
$this->load($filename);
}
}
/**
* Loads a configuration file
*
* @param string $filename filename of the config file
* @return boolean success of the load process
*/
public function load($filename)
{
$environments = false;
$environment = false;
// Sanity checks the config file
if (file_exists($filename) && is_file($filename) && is_readable($filename))
{
require_once $filename;
// Determines the environment
if (isset($config['environment']))
{
$environment = $config['environment'];
}
else
{
if (isset($config['environments']) && is_array($config['environments']))
{
$environments = $config['environments'];
// If we're on the CLI, check an environment was even passed in
if (IS_CLI == true && $_SERVER['argc'] < 2)
{
throw new Exception('You must pass an environment (e.g. php script.php <environment>)');
}
// Loops through the environments and tries to match on IP or name
foreach ($config['environments'] as $name => $hosts)
{
if (!is_array($hosts))
{
$hosts = array($hosts);
}
// Tries to determine the environment name
foreach ($hosts as $host)
{
if (IS_CLI == true)
{
// Checks the first argument on the command line
if ($_SERVER['argv'][1] == $name)
{
$environment = $name;
break;
}
}
else
{
// Exact match
if ((preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $host)
&& $_SERVER['SERVER_ADDR'] == $host)
|| $_SERVER['HTTP_HOST'] == $host)
{
$environment = $name;
break;
}
// Fuzzy match
elseif (substr($host,0,1) == '/' && (preg_match($host, $_SERVER['SERVER_NAME'], $matches) > 0 || preg_match($host, $_SERVER['HTTP_HOST'], $matches) > 0))
{
$environments[$name] = $matches[0];
$environment = $name;
$config['environments'][$name] = $matches[0];
break;
}
}
}
}
}
}
// Flattens the array based on the environment
$this->data = $this->flatten($environment, $config);
// Restore environments value
if ($environments != false)
{
$this->data['environments'] = $environments;
}
// Sets the environment if it's not set already
if (!isset($this->data['environment']))
{
$this->data['environment'] = $environment;
}
// Defaults profiler to true if it doesn't match an option exactly
if (isset($this->data['pickles']['profiler']))
{
if ($this->data['pickles']['profiler'] !== true)
{
// If we have an array convert to a string
if (is_array($this->data['pickles']['profiler']))
{
$this->data['pickles']['profiler'] = implode(',', $this->data['pickles']['profiler']);
}
// Checks that one of our known values exists, if not, force true
if (preg_match('/(objects|timers|queries|explains)/', $this->data['pickles']['profiler'] == false))
{
$this->data['pickles']['profiler'] = true;
}
}
}
else
{
$this->data['pickles']['profiler'] = false;
}
// Defaults expected PICKLES options to false
foreach (array('cache', 'logging', 'minify') as $variable)
{
if (!isset($this->data['pickles'][$variable]))
{
$this->data['pickles'][$variable] = false;
}
}
// Creates constants for the security levels
if (isset($this->data['security']['levels']) && is_array($this->data['security']['levels']))
{
foreach ($this->data['security']['levels'] as $value => $name)
{
$constant = 'SECURITY_LEVEL_' . strtoupper($name);
// Checks if constant is already defined, and throws an error
if (defined($constant))
{
throw new Exception('The constant ' . $constant . ' is already defined');
}
else
{
define($constant, $value);
}
}
}
return true;
}
return false;
}
/**
* Flatten
*
* Flattens the configuration array around the specified environment.
*
* @param string $environment selected environment
* @param array $array configuration error to flatten
* @return array flattened configuration array
*/
private function flatten($environment, $array)
{
if (is_array($array))
{
foreach ($array as $key => $value)
{
if (is_array($value))
{
if (isset($value[$environment]))
{
$value = $value[$environment];
}
else
{
$value = $this->flatten($environment, $value);
}
}
$array[$key] = $value;
}
}
return $array;
}
/**
* Get instance of the object
*
* Let's the parent class do all the work
*
* @static
* @param string $class name of the class to instantiate
* @return object self::$instance instance of the Config class
*/
public static function getInstance($class = 'Config')
{
return parent::getInstance($class);
}
/**
* Magic Setter Method
*
* Prohibits the direct modification of module variables.
*
* @param string $name name of the variable to be set
* @param mixed $value value of the variable to be set
*/
public function __set($name, $value)
{
throw new Exception('Cannot set config variables directly', E_USER_ERROR);
}
/**
* Magic Getter Method
*
* Attempts to load the config variable. If it's not set, will override
* the variable with boolean false.
*
* @param string $name name of the variable requested
* @return mixed value of the variable or boolean false
*/
public function __get($name)
{
if (!isset($this->data[$name]))
{
$this->data[$name] = false;
}
return $this->data[$name];
}
}
?>

498
classes/Controller.php Normal file
View file

@ -0,0 +1,498 @@
<?php
/**
* Single Entry Controller
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Controller Class
*
* The heavy lifter of PICKLES, makes the calls to get the session and
* configuration loaded. Loads modules, serves up user authentication when the
* module asks for it, and loads the viewer that the module requested. Default
* values are present to make things easier on the user.
*
* @usage <code>new Controller($config);</code>
*/
class Controller extends Object
{
/**
* Pass Thru
*
* Whether or not the page being loaded is simple a pass thru for an
* internal PICKLES file. The point of this variable is to suppress the
* profiler report in the destructor.
*
* @access private
* @var boolean
*/
private $passthru = false;
/**
* Constructor
*
* To make life a bit easier when using PICKLES, the Controller logic is
* executed automatically via use of a constructor.
*/
public function __construct()
{
parent::__construct();
// Generate a generic "site down" message if the site is set to be disabled
if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'] == true)
{
Error::fatal($_SERVER['SERVER_NAME'] . ' is currently<br />down for maintenance');
}
// Checks for attributes passed in the URI
if (strstr($_REQUEST['request'], ':'))
{
$parts = explode('/', $_REQUEST['request']);
$_REQUEST['request'] = '';
foreach ($parts as $part)
{
if (strstr($part, ':'))
{
list($variable, $value) = explode(':', $part);
Browser::set($variable, $value);
}
else
{
$_REQUEST['request'] .= ($_REQUEST['request'] ? '/' : '') . $part;
}
}
}
$_REQUEST['request'] = trim($_REQUEST['request']);
// Checks the passed request for validity
if ($_REQUEST['request'])
{
// Catches requests that aren't lowercase
$lowercase_request = strtolower($_REQUEST['request']);
if ($_REQUEST['request'] != $lowercase_request)
{
header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301);
exit;
}
$request = $_REQUEST['request'];
}
// Loads the default module information if we don't have a valid request
else
{
$request = isset($this->config->pickles['module']) ? $this->config->pickles['module'] : 'home';
}
// Loads the module's information
list($module_class, $module_filename, $template_basename, $css_class, $js_basename) = $this->prepareVariables($request);
unset($request);
$module_exists = (isset($module_filename) && $module_filename != null && file_exists($module_filename));
// Instantiates an instance of the module
if ($module_exists)
{
require_once $module_filename;
// Checks that our class exists
if (class_exists($module_class))
{
$module = new $module_class;
}
else
{
if ($this->config->pickles['logging'] === true)
{
Log::warning('Class named ' . $module_class . ' was not found in ' . $module_filename);
}
}
}
// If a new module object wasn't created, create a generic one
if (!isset($module))
{
$module = new Module();
}
// Determines if the module is private and should be, well, private
if ($module->private == true)
{
header('Location: /');
exit;
}
// Determines if we need to serve over HTTP or HTTPS
if ($module->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])
{
header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false))
{
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
// Validates security level
if ($module->security !== false)
{
$is_authenticated = false;
if (is_array($module->security))
{
$module_security = $module->security;
$security_check_class = 'isLevel';
// Checks the type and validates it
if (isset($module_security['type']))
{
$security_check_type = strtoupper($module_security['type']);
if (in_array($security_check_type, array('IS', 'HAS', 'BETWEEN')))
{
$security_check_class = $security_check_type;
}
unset($security_check_type, $module_security['type']);
}
$module_security_levels = array();
// If there's a level(s) key use it
foreach (array('level', 'levels') as $security_level_key)
{
if (isset($module_security[$security_level_key]))
{
if (is_array($module_security[$security_level_key]))
{
array_merge($module_security_levels, $module_security[$security_level_key]);
}
else
{
$module_security_levels[] = $module_security[$security_level_key];
}
unset($module_security[$security_level_key]);
}
}
// Assume everything left in the array is a level and add it to the array
array_merge($module_security_levels, $module_security);
$security_level_count = count($module_security_levels);
switch ($security_check_class)
{
case 'BETWEEN':
if ($security_level_count >= 2)
{
$is_authenticated = Security::betweenLevel($module_security_levels[0], array_pop($module_security_levels));
}
break;
case 'HAS':
if ($security_level_count > 0)
{
$is_authenticated = Security::hasLevel($module_security_levels);
}
break;
case 'IS':
if ($security_level_count > 0)
{
$is_authenticated = Security::isLevel($module_security_levels);
}
break;
}
}
else
{
$is_authenticated = Security::isLevel($module->security);
}
if ($is_authenticated == false)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
exit('{ "status": "error", "message": "You are not properly authenticated" }');
}
else
{
// Sets variable for the destination
$_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/';
// Redirect to login page, potentially configured in the config, else /login
header('Location: /' . (isset($this->config->security['login']) ? $this->config->security['login'] : 'login'));
exit;
}
}
}
// Validates the rendering engine
$engines = is_array($module->engine) ? array_values($module->engine) : array($module->engine);
$engines = array_combine($engines, $engines);
$engine = current($engines);
// Possibly overrides the engine with the passed return type
if (isset($return_type))
{
$return_type = strtoupper($return_type);
// Validates the return type against the module
if (in_array($return_type, array('JSON', 'RSS', 'XML')) && in_array($return_type, $engines))
{
$engine = $return_type;
}
unset($return_type);
}
// Starts up the display engine
$display_class = 'Display_' . $engine;
$display = new $display_class();
// Assigns the template / template variables
$display->setTemplateVariables($module->template, $template_basename, $css_class, $js_basename, $module->fluid);
// Checks the templates
$template_exists = $display->templateExists();
// If there is no valid module or template, then redirect
if (!$module_exists && !$template_exists)
{
if (!$_REQUEST['request'])
{
Error::fatal('Way to go, you\'ve successfully created an infinite redirect loop. Good thing I was here or you would have been served with a pretty ugly browser error.<br /><br />So here\'s the deal, no templates were able to be loaded. Make sure your parent and child templates actually exist and if you\'re using non-default values, make sure they\'re defined correctly in your config.');
}
else
{
$redirect_url = '/';
if (isset($this->config->pickles['404']) && $_REQUEST['request'] != $this->config->pickles['404'])
{
$redirect_url .= $this->config->pickles['404'];
}
header('Location: ' . $redirect_url, 404);
exit;
}
}
// Gets the profiler status
$profiler = $this->config->pickles['profiler'];
$default_method = '__default';
$role_method = null;
if (isset($_SESSION['__pickles']['security']['role']) && !String::isEmpty($_SESSION['__pickles']['security']['role']))
{
$role_method = '__default_' . $_SESSION['__pickles']['security']['role'];
if (method_exists($module, $role_method))
{
$default_method = $role_method;
}
}
// Attempts to execute the default method
if ($default_method == $role_method || method_exists($module, $default_method))
{
if (isset($requested_id))
{
$module->setRequest(array('id' => $requested_id));
}
// Starts a timer before the module is executed
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('module ' . $default_method);
}
$valid_request = false;
$valid_security_hash = false;
$error_message = 'An unexpected error has occurred';
// Determines if the request method is valid for this request
if ($module->method != false)
{
$methods = (is_array($module->method) ? $module->method : array($module->method));
$request_method = $_SERVER['REQUEST_METHOD'];
foreach ($methods as $method)
{
if ($request_method == strtoupper($method))
{
$valid_request = true;
break;
}
}
if ($valid_request == false)
{
$error_message = 'There was a problem with your request method';
}
unset($methods, $request_method, $method);
}
else
{
$valid_request = true;
}
// Validates the hash if applicable
if ($module->hash != false)
{
if (isset($_REQUEST['security_hash']))
{
$hash_value = ($module->hash === true ? get_class($module) : $module->hash);
if (Security::generateHash($hash_value) == $_REQUEST['security_hash'])
{
$valid_security_hash = true;
}
else
{
$error_message = 'Invalid security hash';
}
unset($hash_value);
}
else
{
$error_message = 'Missing security hash';
}
}
else
{
$valid_security_hash = true;
}
/**
* Note to Self: When building in caching will need to let the
* module know to use the cache, either passing in a variable
* or setting it on the object
*/
if ($valid_request && $valid_security_hash)
{
$module_return = $module->$default_method();
if (!is_array($module_return))
{
$module_return = $module->return;
}
else
{
$module_return = array_merge($module_return, $module->return);
}
}
$display->setModuleReturn(isset($module_return) ? $module_return : array('status' => 'error', 'message' => $error_message));
unset($error_message);
// Stops the module timer
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('module ' . $default_method);
}
// Sets meta data from the module
$display->setMetaData(array(
'title' => $module->title,
'description' => $module->description,
'keywords' => $module->keywords
));
}
// Starts a timer for the display rendering
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('display render');
}
// Renders the content
$display->render();
// Steps the display timer
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('display render');
}
}
/**
* Destructor
*
* Dumps out the Profiler's report if applicable.
*/
public function __destruct()
{
parent::__destruct();
// Display the Profiler's report is the stars are aligned
if ($this->config->pickles['profiler'] != false && $this->passthru == false)
{
Profiler::report();
}
}
/**
* Prepare Variables
*
* Processes the request variable and creates all the variables that the
* Controller needs to load the page.
*
* @param string $basename the requested page
* @return array the resulting variables
*/
public function prepareVariables($basename)
{
if (strpos($basename, '.') !== false)
{
list($basename, $action) = explode('.', $basename, 2);
$action = str_replace('.', '_', $action);
}
// Sets up all of our variables
$module_class = strtr($basename, '/', '_');
$module_filename = SITE_MODULE_PATH . $basename . '.php';
$template_basename = $basename;
$css_class = $module_class;
$js_basename = $basename;
if (isset($action))
{
$module_class .= '_' . $action;
$template_basename .= '/' . $action;
$css_class .= '_' . $action;
$js_basename .= '/' . $action;
}
// Scrubs class names with hyphens
if (strpos($module_class, '-') !== false)
{
$module_class = preg_replace('/(-(.{1}))/e', 'strtoupper("$2")', $module_class);
}
return array($module_class, $module_filename, $template_basename, $css_class, $js_basename);
}
}
?>

153
classes/Convert.php Normal file
View file

@ -0,0 +1,153 @@
<?php
/**
* Converter
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Convert Class
*
* Collection of statically called methods to help aid in converting data
* formats.
*/
class Convert
{
// {{{ Array to XML
/**
* Array to XML
*
* Converts an array into XML tags (recursive). This method expects the
* passed array to be formatted very specifically to accomodate the fact
* that an array's format isn't quite the same as well-formed XML.
*
* Input Array =
* array('children' => array(
* 'child' => array(
* array('name' => 'Wendy Darling'),
* array('name' => 'John Darling'),
* array('name' => 'Michael Darling')
* )
* ))
*
* Output XML =
* <children>
* <child><name>Wendy Darling</name></child>
* <child><name>John Darling</name></child>
* <child><name>Michael Darling</name></child>
* </children>
*
* @static
* @param array $array array to convert into XML
* @return string generated XML
*/
public static function arrayToXML($array, $format = false, $level = 0)
{
$xml = '';
if (is_array($array))
{
foreach ($array as $node => $value)
{
// Checks if the value is an array
if (is_array($value))
{
foreach ($value as $node2 => $value2)
{
if (is_array($value2))
{
// Nest the value if the node is an integer
$new_value = (is_int($node2) ? $value2 : array($node2 => $value2));
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '<' . $node . '>' . ($format ? "\n" : '');
$xml .= self::arrayToXML($new_value, $format, $level + 1);
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '</' . $node . '>' . ($format ? "\n" : '');
}
else
{
if (is_int($node2))
{
$node2 = $node;
}
// Checks for special characters
if (htmlspecialchars($value2) != $value2)
{
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '<' . $node2 . '><![CDATA[' . $value2 . ']]></' . $node2 . '>' . ($format ? "\n" : '');
}
else
{
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '<' . $node2 . '>' . $value2 . '</' . $node2 . '>' . ($format ? "\n" : '');
}
}
}
}
else
{
// Checks for special characters
if (htmlspecialchars($value) != $value)
{
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '<' . $node . '><![CDATA[' . $value . ']]></' . $node . '>' . ($format ? "\n" : '');
}
else
{
$xml .= ($format ? str_repeat("\t", $level) : '');
$xml .= '<' . $node . '>' . $value . '</' . $node . '>' . ($format ? "\n" : '');
}
}
}
}
return $xml;
}
// }}}
// {{{ To JSON
/**
* To JSON
*
* Encodes passed variable as JSON.
*
* Requires PHP 5 >= 5.2.0 or PECL json >= 1.2.0
*
* @link http://json.org/
* @link http://us.php.net/json_encode
* @link http://pecl.php.net/package/json
*
* @static
* @param mixed $variable variable to convert
* @return JSON encoded string
*/
public static function toJSON($variable)
{
if (JSON_AVAILABLE)
{
return json_encode($variable);
}
else
{
return '{ "status": "error", "message": "json_encode() not found" }';
}
}
// }}}
}
?>

131
classes/Database.php Normal file
View file

@ -0,0 +1,131 @@
<?php
/**
* Database Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Database Factory
*
* Generic class to simplify connecting to a database. All database objects
* should be created by this class to future proof against any internal changes
* to PICKLES.
*/
class Database extends Object
{
/**
* Constructor
*
* Attempts to get an instance of the passed database type or attempts to
* use a default specified in the config.
*
* @param string $name optional name of the connection to use
*/
public function __construct(String $name = null)
{
parent::__construct();
return Database::getInstance($name);
}
/**
* Get Instance
*
* Looks up the datasource using the passed name and gets an instance of
* it. Allows for easy sharing of certain classes within the system to
* avoid the extra overhead of creating new objects each time. Also avoids
* the hassle of passing around variables (yeah I know, very global-ish)
*
* @static
* @param string $name name of the datasource
* @return object instance of the class
*/
public static function getInstance($name = null)
{
$config = Config::getInstance();
// Checks if we have a default
if ($name == null)
{
// Checks the config for a default
if (isset($config->pickles['datasource']))
{
$name = $config->pickles['datasource'];
}
// Tries to use the first defined datasource
elseif (is_array($config->datasources))
{
$datasources = $config->datasources;
$name = key($datasources);
}
}
// If we have a name try to set up a connection
if ($name != null)
{
if (isset($config->datasources[$name]))
{
$datasource = $config->datasources[$name];
if (!isset($datasource['driver']))
{
return false;
}
$datasource['driver'] = strtolower($datasource['driver']);
if (!isset(self::$instances['Database'][$name]))
{
// Checks the driver is legit and scrubs the name
switch ($datasource['driver'])
{
case 'pdo_mysql': $class = 'PDO_MySQL'; break;
case 'pdo_pgsql': $class = 'PDO_PostgreSQL'; break;
case 'pdo_sqlite': $class = 'PDO_SQLite'; break;
default:
throw new Exception('Datasource driver "' . $datasource['driver'] . '" is invalid');
break;
}
// Instantiates our database class
$class = 'Database_' . $class;
$instance = new $class();
// Sets our database parameters
if (is_array($datasource))
{
foreach ($datasource as $variable => $value)
{
$instance->$variable = $value;
}
}
}
// Caches the instance for possible reuse later
if (isset($instance))
{
self::$instances['Database'][$name] = $instance;
}
// Returns the instance
return self::$instances['Database'][$name];
}
}
return false;
}
}
?>

139
classes/Database/Common.php Normal file
View file

@ -0,0 +1,139 @@
<?php
/**
* Common Database Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Common Database Abstraction Layer
*
* Parent class that our database driver classes should be extending. Contains
* basic functionality for instantiation and interfacing.
*/
abstract class Database_Common extends Object
{
/**
* Driver
*
* @var string
*/
public $driver = null;
/**
* Hostname for the server
*
* @var string
*/
public $hostname = 'localhost';
/**
* Port number for the server
*
* @var integer
*/
public $port = null;
/**
* UNIX socket for the server
*
* @var integer
*/
public $socket = null;
/**
* Username for the server
*
* @var string
*/
public $username = null;
/**
* Password for the server
*
* @var string
*/
public $password = null;
/**
* Database name for the server
*
* @var string
*/
public $database = null;
/**
* Whether or not to use caching
*
* @var boolean
*/
public $cache = false;
/**
* Connection resource
*
* @var object
*/
public $connection = null;
/**
* Results object for the executed statement
*
* @var object
*/
public $results = null;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// Checks the driver is set and available
if ($this->driver == null)
{
throw new Exception('Driver name is not set');
}
else
{
if (extension_loaded($this->driver) == false)
{
throw new Exception('Driver "' . $this->driver . '" is not loaded');
}
}
}
/**
* Open Database Connection
*
* Establishes a connection to the MySQL database based on the configuration
* options that are available in the Config object.
*
* @abstract
* @return boolean true on success, throws an exception overwise
*/
abstract public function open();
/**
* Close Database Connection
*
* Sets the connection to null regardless of state.
*
* @abstract
* @return boolean always true
*/
abstract public function close();
}
?>

View file

@ -0,0 +1,278 @@
<?php
/**
* PDO Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* PDO Abstraction Layer
*
* Parent class for any of our database classes that use PDO.
*/
class Database_PDO_Common extends Database_Common
{
/**
* DSN format
*
* @access protected
* @var string
*/
protected $dsn;
/**
* PDO Attributes
*
* @access protected
* @var string
*/
protected $attributes = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::NULL_EMPTY_STRING => true,
);
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// Checks that the prefix is set
if ($this->dsn == null)
{
throw new Exception('Data source name is not defined');
}
switch ($this->driver)
{
case 'pdo_mysql':
// Resolves "Invalid UTF-8 sequence" issues when encoding as JSON
// @todo Didn't resolve that issue, borked some other characters though
//$this->attributes[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8';
break;
case 'pdo_pgsql':
// This combats a bug: https://bugs.php.net/bug.php?id=62571&edit=1
$this->attributes[PDO::ATTR_PERSISTENT] = false;
// This allows for multiple prepared queries
$this->attributes[PDO::ATTR_EMULATE_PREPARES] = true;
break;
}
}
/**
* Opens database connection
*
* Establishes a connection to the database based on the set configuration
* options.
*
* @return boolean true on success, throws an exception overwise
*/
public function open()
{
if ($this->connection === null)
{
if (isset($this->username, $this->password, $this->database))
{
// Creates a new PDO database object (persistent)
try
{
// Swaps out any variables with values in the DSN
$this->dsn = str_replace(
array('[[hostname]]', '[[port]]', '[[socket]]', '[[username]]', '[[password]]', '[[database]]'),
array($this->hostname, $this->port, $this->socket, $this->username, $this->password, $this->database),
$this->dsn
);
// Strips any empty parameters in the DSN
$this->dsn = str_replace(array('host=;', 'port=;', 'unix_socket=;'), '', $this->dsn);
// Attempts to establish a connection
$this->connection = new PDO($this->dsn, $this->username, $this->password, $this->attributes);
}
catch (PDOException $e)
{
throw new Exception($e);
}
}
else
{
throw new Exception('There was an error loading the database configuration');
}
}
return true;
}
/**
* Closes database connection
*
* Sets the connection to null regardless of state.
*
* @return boolean always true
*/
public function close()
{
$this->connection = null;
return true;
}
/**
* Executes an SQL Statement
*
* Executes a standard or prepared query based on passed parameters. All
* queries are logged to a file as well as timed and logged in the
* execution time is over 1 second.
*
* @param string $sql statement to execute
* @param array $input_parameters optional key/values to be bound
* @return integer ID of the last inserted row or sequence number
*/
public function execute($sql, $input_parameters = null)
{
$this->open();
if ($this->config->pickles['logging'] === true)
{
$loggable_query = $sql;
if ($input_parameters != null)
{
$loggable_query .= ' -- ' . (JSON_AVAILABLE ? json_encode($input_parameters) : serialize($input_parameters));
}
Log::query($loggable_query);
}
$sql = trim($sql);
// Checks if the query is blank
if ($sql != '')
{
$files = array();
// Ubuntu 10.04 is a bit behind on PHP 5.3.x and the IGNORE_ARGS
// constant doesn't exist. To conserve memory, the backtrace will
// Only be used on servers running PHP 5.3.6 or above.
if (version_compare(PHP_VERSION, '5.3.6', '>='))
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
krsort($backtrace);
foreach ($backtrace as $file)
{
if (isset($file['class'], $file['line']))
{
$files[] = $file['class'] . ':' . $file['line'];
}
}
}
$sql .= "\n" . '/* [' . implode('|', $files) . '] */';
try
{
// Establishes if we're working on an EXPLAIN
if (Profiler::enabled('explains') == true)
{
$explaining = preg_match('/^EXPLAIN /i', $sql);
$selecting = preg_match('/^SELECT /i', $sql);
}
else
{
$explaining = null;
$selecting = null;
}
// Executes a standard query
if ($input_parameters === null)
{
// Explains the query
if ($selecting == true && $explaining == false)
{
$explain = $this->fetch('EXPLAIN ' . $sql);
}
$start_time = microtime(true);
$this->results = $this->connection->query($sql);
}
// Executes a prepared statement
else
{
// Explains the query
if ($selecting == true && $explaining == false)
{
$explain = $this->fetch('EXPLAIN ' . $sql, $input_parameters);
}
$start_time = microtime(true);
$this->results = $this->connection->prepare($sql);
$this->results->execute($input_parameters);
}
$end_time = microtime(true);
$duration = $end_time - $start_time;
if ($this->config->pickles['logging'] === true && $duration >= 1)
{
Log::slowQuery($duration . ' seconds: ' . $loggable_query);
}
// Logs the information to the profiler
if ($explaining == false && Profiler::enabled('explains', 'queries'))
{
Profiler::logQuery($sql, $input_parameters, (isset($explain) ? $explain : false), $duration);
}
}
catch (PDOException $e)
{
throw new Exception($e);
}
}
else
{
throw new Exception('No query to execute');
}
return $this->connection->lastInsertId();
}
/**
* Fetch records from the database
*
* @param string $sql statement to be executed
* @param array $input_parameters optional key/values to be bound
* @param string $return_type optional type of return set
* @return mixed based on return type
*/
public function fetch($sql = null, $input_parameters = null)
{
$this->open();
if ($sql !== null)
{
$this->execute($sql, $input_parameters);
}
// Pulls the results based on the type
$results = $this->results->fetchAll(PDO::FETCH_ASSOC);
return $results;
}
}
?>

View file

@ -0,0 +1,45 @@
<?php
/**
* MySQL Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* MySQL Database Abstraction Layer
*/
class Database_PDO_MySQL extends Database_PDO_Common
{
/**
* Driver
*
* @var string
*/
public $driver = 'pdo_mysql';
/**
* DSN format
*
* @var string
*/
public $dsn = 'mysql:host=[[hostname]];port=[[port]];unix_socket=[[socket]];dbname=[[database]]';
/**
* Default port
*
* @var integer
*/
public $port = 3306;
}
?>

View file

@ -0,0 +1,45 @@
<?php
/**
* PostgreSQL Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* PostgreSQL Database Abstraction Layer
*/
class Database_PDO_PostgreSQL extends Database_PDO_Common
{
/**
* Driver
*
* @var string
*/
public $driver = 'pdo_pgsql';
/**
* DSN format
*
* @var string
*/
public $dsn = 'pgsql:host=[[hostname]];port=[[port]];dbname=[[database]];user=[[username]];password=[[password]]';
/**
* Default port
*
* @var integer
*/
public $port = 5432;
}
?>

View file

@ -0,0 +1,38 @@
<?php
/**
* SQLite Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* SQLite Database Abstraction Layer
*/
class Database_PDO_SQLite extends Database_PDO_Common
{
/**
* Driver
*
* @var string
*/
public $driver = 'pdo_sqlite';
/**
* DSN format
*
* @var string
*/
public $dsn = 'sqlite:[[hostname]]';
}
?>

41
classes/Date.php Normal file
View file

@ -0,0 +1,41 @@
<?php
/**
* Date Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Date Class
*
* Just a simple collection of static functions to accomplish some of the more
* redundant date related manipulation.
*/
class Date
{
/**
* Age
*
* Calculates age based on the passed date.
*
* @static
* @param string $date birth / inception date
* @return integer $age number of years old
*/
public static function age($date)
{
return Time::age($date);
}
}
?>

199
classes/Display/Common.php Normal file
View file

@ -0,0 +1,199 @@
<?php
/**
* Common Display Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Common Display Class
*
* This is the parent class class that each display class should be
* extending and executing parent::render()
*/
abstract class Display_Common extends Object
{
/**
* Template Extension
*
* @access protected
* @var string $extension file extension for the template files
*/
protected $extension = null;
/**
* Parent Template
*
* @access protected
* @var string
*/
protected $parent_template = null;
/**
* Child (sub) Template
*
* @access protected
* @var string
*/
protected $child_template = null;
/**
* CSS Class Name
*
* @access protected
* @var string
*/
protected $css_class = '';
/**
* Javascript [Path and] Basename
*
* @access protected
* @var array
*/
protected $js_basename = '';
/**
* Meta Data
*
* @access protected
* @var array
*/
protected $meta_data = null;
/**
* Module Return Data
*
* @access protected
* @var array
*/
protected $module_return = null;
/**
* Constructor
*
* Gets those headers working
*/
public function __construct()
{
parent::__construct();
// Obliterates any passed in PHPSESSID (thanks Google)
if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') !== false)
{
list($request_uri, $phpsessid) = explode('?PHPSESSID=', $_SERVER['REQUEST_URI'], 2);
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $request_uri);
exit;
}
else
{
// XHTML compliancy stuff
ini_set('arg_separator.output', '&amp;');
ini_set('url_rewriter.tags', 'a=href,area=href,frame=src,input=src,fieldset=');
header('Content-type: text/html; charset=UTF-8');
}
}
/**
* Set Template
*
* Sets the template file based on passed template type. Adds path and
* extension if applicable.
*
* @param string $template template file's basename
* @param string $type template file's type (either parent or child)
*/
private function setTemplate($template, $type)
{
if ($template != null)
{
$template_name = $type . '_template';
$template_path = SITE_TEMPLATE_PATH . ($type == 'parent' ? '__shared/' : '');
$template_file = $template_path . $template . ($this->extension != false ? '.' . $this->extension : '');
if (file_exists($template_file))
{
$this->$template_name = $template_file;
}
}
}
/**
* Set Template Variables
*
* Sets the variables used by the templates
*
* @param string $parent_template parent template
* @param string $child_template child (sub) template
* @param string $css_class name of the CSS class for the module
* @param string $js_basename basename for the javascript file for the module
* @param boolean $fluid whether or not use a fluid layout
*/
public function setTemplateVariables($parent_template, $child_template, $css_class, $js_basename, $fluid)
{
$this->setTemplate($parent_template, 'parent');
$this->setTemplate($child_template, 'child');
$this->css_class = $css_class;
$this->js_basename = $js_basename;
$this->fluid = $fluid;
}
/**
* Set Meta Data
*
* Sets the meta data from the module so the display class can use it
*
* @param array $meta_data key/value array of data
*/
public function setMetaData($meta_data)
{
$this->meta_data = $meta_data;
}
/**
* Set Module Return
*
* Sets the return data from the module so the display class can display it
*
* @param array $module_return key / value pairs for the data
*/
public function setModuleReturn($module_return)
{
$this->module_return = $module_return;
}
/**
* Template Exists
*
* Checks the templates for validity, not required by every display type so
* the return defaults to true.
*
* @return boolean whether or not the template exists
*/
public function templateExists()
{
return true;
}
/**
* Rendering Method
*
* @abstract
*/
abstract public function render();
}
?>

34
classes/Display/JSON.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/**
* JSON Display Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* JSON Display
*
* Displays data in JavaScript Object Notation.
*/
class Display_JSON extends Display_Common
{
/**
* Renders the data in JSON format
*/
public function render()
{
echo Convert::toJSON($this->module_return);
}
}
?>

119
classes/Display/PHP.php Normal file
View file

@ -0,0 +1,119 @@
<?php
/**
* PHP Display Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* PHP Display
*
* Displays the associated PHP templates for the Model.
*/
class Display_PHP extends Display_Common
{
/**
* Template Extension
*
* I know there's some controversy amoungst my peers concerning the
* usage of the .phtml extension for these PHP template files. If you
* would prefer .php or .tpl extensions, feel free to void your
* warranty and change it here.
*
* @access protected
* @var string $extension file extension for the template files
*/
protected $extension = 'phtml';
/**
* Template Exists
*
* @return integer the number of templates defined
*/
public function templateExists()
{
if ($this->parent_template != null)
{
return file_exists($this->parent_template) && file_exists($this->child_template);
}
else
{
return file_exists($this->child_template);
}
}
/**
* Renders the PHP templated pages
*/
public function render()
{
if ($this->templateExists())
{
// Starts up the buffer
ob_start();
// Puts the class variables in local scope of the template
$__config = $this->config;
$__meta = $this->meta_data;
$__module = $this->module_return;
$__css_class = $this->css_class;
$__js_file = $this->js_basename;
$__fluid = $this->fluid;
// Creates (possibly overwritten) objects
$dynamic_class = (class_exists('CustomDynamic') ? 'CustomDynamic' : 'Dynamic');
$form_class = (class_exists('CustomForm') ? 'CustomForm' : 'Form');
$html_class = (class_exists('CustomHTML') ? 'CustomHTML' : 'HTML');
$__dynamic = new $dynamic_class();
$__form = new $form_class();
$__html = new $html_class();
// Loads the template
if ($this->parent_template != null)
{
if ($this->child_template == null)
{
$__template = $this->parent_template;
}
else
{
$__template = $this->child_template;
}
require_once $this->parent_template;
}
elseif ($this->child_template != null)
{
$__template = $this->child_template;
require_once $__template;
}
// Grabs the buffer contents and clears it out
$buffer = ob_get_clean();
// Kills any whitespace and HTML comments
$buffer = preg_replace(array('/^[\s]+/m', '/<!--(?:(?!BuySellAds).)+-->/U'), '', $buffer);
// Note, this doesn't exit in case you want to run code after the display of the page
echo $buffer;
}
else
{
echo Convert::toJSON($this->module_return);
}
}
}
?>

146
classes/Display/RSS.php Normal file
View file

@ -0,0 +1,146 @@
<?php
/**
* RSS Display Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* RSS Display
*
* Displays data as an RSS formatted XML string.
*/
class Display_RSS extends Display_Common
{
// {{{ Feed Defaults
/**
* RSS Version
*
* @access private
* @var string
*/
private $version = '2.0';
/**
* Date Format
*
* @access private
* @var string
*/
private $date_format = 'r';
// }}}
// {{{ Channel Defaults
/**
* Title
*
* @access private
* @var string
*/
private $title = '';
/**
* Link
*
* @access private
* @var string
*/
private $link = '';
/**
* Description
*
* @access private
* @var string
*/
private $description = '';
/**
* Language
*
* @access private
* @var string
*/
private $language = 'en-us';
/**
* Generator
*
* @access private
* @var string
*/
private $generator = 'https://github.com/joshtronic/pickles';
// }}}
/**
* Renders the data in RSS format
*/
public function render()
{
// Throws off the syntax highlighter otherwise
echo '<' . '?xml version="1.0" ?' . '><rss version="' . $this->version . '"><channel>';
// Loops through the passable channel variables
$channel = array();
foreach (array('title', 'link', 'description', 'language') as $variable)
{
if (isset($this->module_return[$variable]))
{
$this->$variable = $this->module_return[$variable];
}
$channel[$variable] = $this->$variable;
}
$channel['generator'] = $this->generator;
// Loops through the items
$items = '';
$build_date = '';
if (isset($this->module_return['items']) && is_array($this->module_return['items']))
{
foreach ($this->module_return['items'] as $item)
{
// Note: time is the equivalent to pubDate, I just don't like camel case variables
$publish_date = date($this->date_format, is_numeric($item['time']) ? $item['time'] : strtotime($item['time']));
if ($build_date == '')
{
$build_date = $publish_date;
}
if (isset($item['link']))
{
$item['guid'] = $item['link'];
}
$item['pubDate'] = $publish_date;
unset($item['time']);
$items .= Convert::arrayToXML($item);
}
}
$channel['pubDate'] = $build_date;
$channel['lastBuildDate'] = $build_date;
echo Convert::arrayToXML($channel) . $items . '</channel></rss>';
}
}
?>

34
classes/Display/XML.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/**
* XML Display Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* XML Display
*
* Displays data in XML format.
*/
class Display_XML extends Display_Common
{
/**
* Renders the data in XML format
*/
public function render()
{
echo Convert::arrayToXML($this->module_return);
}
}
?>

144
classes/Distance.php Normal file
View file

@ -0,0 +1,144 @@
<?php
/**
* Distance
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Distance Class
*
* Collection of statically called methods to help aid distance-related
* conversions and calculations.
*/
class Distance
{
// {{{ Call Static
/**
* Call Static
*
* Magic method to power the unit conversion without much code.
*
* @static
* @param string $method name of the static method being called
* @param array $arguments array of the passed arguments
* @return mixed converted units or false
*/
public static function __callStatic($method, $arguments)
{
$pieces = explode('to', strtolower($method));
if (count($pieces) == 2)
{
return Distance::convertUnit($arguments[0], $pieces[0], $pieces[1]);
}
return false;
}
// }}}
// {{{ Convert Unit
/**
* Convert Unit
*
* Converts a distance from one unit to another.
*
* @static
* @param mixed $distance starting distance
* @param string $from starting unit
* @param string $to ending unit
* @return mixed
*/
private static function convertUnit($distance, $from, $to)
{
$multiplier = 1;
switch ($from)
{
case 'kilometers':
switch ($to)
{
case 'miles': $multiplier = 0.621371; break;
case 'meters': $multiplier = 1000; break;
case 'yards': $multiplier = 1093.61; break;
}
break;
case 'miles':
switch ($to)
{
case 'kilometers': $multiplier = 1.60934; break;
case 'meters': $multiplier = 1609.34; break;
case 'yards': $multiplier = 1760; break;
}
break;
case 'meters':
switch ($to)
{
case 'kilometers': $multiplier = 0.001; break;
case 'miles': $multiplier = 0.000621371; break;
case 'yards': $multiplier = 1.09361; break;
}
break;
}
return $distance * $multiplier;
}
// }}}
// {{{ Calculate Distance
/**
* Calculate Distance
*
* Calculates the distance between two sets of coordinates and returns the
* requested units. I really wanted to call this distance() but it seems
* you can't do that in PHP due to the backwards compatibility of the
* PHP4 constructors that were named the same as the class.
*
* @static
* @param mixed $latitude_from starting latitude
* @param mixed $longitude_from starting longitude
* @param mixed $latitude_to ending latitude
* @param mixed $longitude_to ending longitude
* @param string $unit optional units to return, miles by default
* @return mixed distance between the points in the desired unit
*/
public static function calculateDistance($latitude_from, $longitude_from, $latitude_to, $longitude_to, $unit = 'miles')
{
$unit = ucwords(strtolower($unit));
$theta = $longitude_from - $longitude_to;
$distance =
sin(deg2rad($latitude_from))
* sin(deg2rad($latitude_to))
+ cos(deg2rad($latitude_from))
* cos(deg2rad($latitude_to))
* cos(deg2rad($theta));
$distance = acos($distance);
$distance = rad2deg($distance);
$miles = $distance * 60 * 1.1515;
$method = 'milesTo' . $unit;
return Distance::$method($miles);
}
// }}}
}
?>

305
classes/Dynamic.php Normal file
View file

@ -0,0 +1,305 @@
<?php
/**
* Dynamic Content Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Dynamic Class
*
* Handles generating links to static content that are a timestamp injected as
* to avoid hard caching. Also minifies content where applicable.
*
* Note: you will want to add a mod_rewrite line to your .htaccess to support
* the routing to the filenames with the timestamp injected:
*
* RewriteRule ^(.+)\.([\d]+)\.(css|js|gif|png|jpg|jpeg)$ /$1.$3 [NC,QSA]
*/
class Dynamic extends Object
{
/**
* Generate Reference
*
* Appends a dynamic piece of information to the passed reference in the
* form of a UNIX timestamp added to the query string.
*
* @param string $reference URI reference of the file
* @param string $failover URI reference to use if the reference can't be found
* @return string URI reference reference with dynamic content
*/
public function reference($reference, $failover = false)
{
// Checks if the URI reference is absolute, and not relative
if (substr($reference, 0, 1) == '/')
{
$query_string = '';
// Checks for ? and extracts query string
if (strstr($reference, '?'))
{
list($reference, $query_string) = explode('?', $reference);
}
// Adds the dot so the file functions can find the file
$file = '.' . $reference;
if (file_exists($file))
{
// Replaces the extension with time().extension
$parts = explode('.', $reference);
if (count($parts) == 1)
{
throw new Exception('Filename must have an extension (e.g. /path/to/file.png)');
}
else
{
end($parts);
$parts[key($parts)] = filemtime($file) . '.' . current($parts);
$reference = implode('.', $parts);
}
// Adds the query string back
if ($query_string != '')
{
$reference .= '?' . $query_string;
}
}
else
{
if ($failover != false)
{
$reference = $failover;
}
else
{
throw new Exception('Supplied reference does not exist (' . $reference . ')');
}
}
}
else
{
throw new Exception('Reference value must be absolute (e.g. /path/to/file.png)');
}
return $reference;
}
/**
* Generate Stylesheet Reference
*
* Attempts to minify the stylesheet and then returns the reference URI for
* the file, minified or not. Supports LESS and SASS, pass it a .less file
* or a .scss file instead and it will be compiled before minification.
*
* @param string $reference URI reference of the Stylesheet
* @return string URI reference reference with dynamic content
* @url http://lesscss.org
* @url http://sass-lang.com
*/
public function css($original_reference)
{
$less = false;
$sass = false;
// Injects .min into the filename
$parts = explode('.', $original_reference);
if (count($parts) == 1)
{
throw new Exception('Filename must have an extension (e.g. /path/to/file.css)');
}
else
{
end($parts);
switch (current($parts))
{
case 'less':
$less = true;
$parts[key($parts)] = 'css';
break;
case 'scss':
$sass = true;
$parts[key($parts)] = 'css';
break;
}
$parts[key($parts)] = 'min.' . current($parts);
$minified_reference = implode('.', $parts);
}
$original_filename = '.' . $original_reference;
$minified_filename = '.' . $minified_reference;
$path = dirname($original_filename);
if (file_exists($original_filename))
{
$reference = $original_reference;
/**
* Disabled the sanity checks because I'm using LESS's @import for
* some hackery and it's not validating as true due to the imported
* file not being interrogated. Should be okay as minifying is now
* a subjective action that's turned on in the config due to the
* issues I had in production with it.
*
* if (is_writable($path)
* && (!file_exists($minified_filename) || filemtime($original_filename) > filemtime($minified_filename))
* && $this->config->pickles['minify'] === true)
*/
if ($this->config->pickles['minify'] === true)
{
// Compiles LESS & SASS to CSS before minifying
if ($less || $sass)
{
$compiled_filename = str_replace('.min', '', $minified_filename);
if ($less)
{
// I couldn't get getenv() to give me the PATH value... so yeah, there's that.
exec('echo $PATH', $path);
putenv('PATH=' . $path[0] . PATH_SEPARATOR . '/usr/local/bin');
$command = 'lessc ' . $original_filename . ' > ' . $compiled_filename;
}
elseif ($sass)
{
$command = 'sass ' . $original_filename . ':' . $compiled_filename;
}
exec($command, $output, $return);
if ($return !== 0)
{
throw new Exception('There was an error executing `' . $command . '` it returned exit code ' . $return);
}
$original_filename = $compiled_filename;
}
// Minifies CSS with a few basic character replacements.
$stylesheet = file_get_contents($original_filename);
$stylesheet = str_replace(array("\t", "\n", ', ', ' {', ': ', ';}', '{ ', '; '), array('', '', ',', '{', ':', '}', '{', ';'), $stylesheet);
$stylesheet = preg_replace('/\/\*.+?\*\//', '', $stylesheet);
file_put_contents($minified_filename, $stylesheet);
$reference = $minified_reference;
}
elseif (file_exists($minified_filename))
{
$reference = $minified_reference;
}
else
{
if ($this->config->pickles['logging'] === true)
{
Log::warning('Unable to minify ' . $original_reference . ' and a minified copy does not already exist');
}
}
$reference = $this->reference($reference);
}
else
{
throw new Exception('Supplied reference does not exist');
}
return $reference;
}
/**
* Generate Javascript Reference
*
* Attempts to minify the source with Google's Closure compiler, and then
* returns the reference URI for the file, minified or not.
*
* @link http://code.google.com/closure/compiler/
* @param string $reference URI reference of the Javascript file
* @return string URI reference reference with dynamic content
*/
public function js($original_reference, $level = 'simple')
{
$level = strtoupper($level);
switch ($level)
{
CASE 'WHITESPACE':
CASE 'SIMPLE':
CASE 'ADVANCED':
// Injects .min into the filename
$parts = explode('.', $original_reference);
if (count($parts) == 1)
{
throw new Exception('Filename must have an extension (e.g. /path/to/file.js)');
}
else
{
end($parts);
$parts[key($parts)] = 'min.' . current($parts);
$minified_reference = implode('.', $parts);
}
$original_filename = '.' . $original_reference;
$minified_filename = '.' . $minified_reference;
$path = dirname($original_filename);
if (file_exists($original_filename))
{
$reference = $original_reference;
if (is_writable($path)
&& (!file_exists($minified_filename) || filemtime($original_filename) > filemtime($minified_filename))
&& extension_loaded('curl')
&& $this->config->pickles['minify'] === true)
{
exec('java -jar ' . PICKLES_PATH . 'vendors/google/closure-compiler/compiler.jar --js=' . $original_filename . ' --compilation_level=' . ($level . '_' . ($level == 'WHITESPACE' ? 'ONLY' : 'OPTIMIZATIONS')) . ' --js_output_file=' . $minified_filename);
$reference = $minified_reference;
}
elseif (file_exists($minified_filename))
{
$reference = $minified_reference;
}
else
{
if ($this->config->pickles['logging'] === true)
{
Log::warning('Unable to minify ' . $original_reference . ' and a minified copy does not already exist');
}
}
$reference = $this->reference($reference);
}
else
{
throw new Exception('Supplied reference does not exist');
}
break;
default:
throw new Exception('The level "' . $level . '" is invalid. Valid levels include "whitespace", "simple" and "advanced"');
break;
}
return $reference;
}
}
?>

104
classes/Error.php Normal file
View file

@ -0,0 +1,104 @@
<?php
/**
* Error Reporting for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Error Class
*
* Standardized error reporting, mostly used to display fatal errors.
*/
class Error
{
/**
* Fatal Error
*
* Displays a friendly error to the user via HTML, logs it then exits.
*
* @static
* @param string $message the message to be displayed to the user
*/
public static function fatal($message)
{
$config = Config::getInstance();
if ($config->pickles['logging'] === true)
{
if (Log::error($message) == false)
{
$message .= '<br /><br />This error message could not be logged as the log path or log file is not writable';
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo $_SERVER['SERVER_NAME']; ?> - error</title>
<style>
html
{
background: #eee;
font-family: "Lucida Sans", "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, sans-serif;
width: 100%;
height: 100%;
font-size: 1em;
}
body
{
text-align: center;
margin-top: 100px;
}
div
{
font-size: 150%;
color: #600;
text-shadow: 2px 2px 2px #eb8383;
margin: 0;
font-weight: bold;
background: #ff9c9c;
padding: 20px;
border-radius: 20px;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
width: 550px;
margin: 0 auto;
border: 3px solid #890f0f;
}
h1, a
{
font-size: 70%;
color: #999;
text-decoration: none;
}
a:hover
{
color: #000;
}
</style>
</head>
<body>
<h1><?php echo $_SERVER['SERVER_NAME']; ?></h1>
<div><?php echo $message; ?></div>
<a href="https://github.com/joshtronic/pickles" target="_blank">Powered by PICKLES</a>
</body>
</html>
<?php
exit;
}
}
?>

70
classes/File.php Normal file
View file

@ -0,0 +1,70 @@
<?php
/**
* File Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* File Class
*
* Just a simple collection of static functions to accomplish some of the more
* redundant file related manipulation.
*/
class File
{
/**
* Remove a Directory, Recursively
*
* Removes a directory by emptying all of the contents recursively and then
* removing the directory, as PHP will not let you rmdir() on ain non-empty
* directory. Use with caution, seriously.
*
* @static
* @param string $directory directory to remove
* @return boolean status of the final rmdir();
*/
public static function removeDirectory($directory)
{
if (substr($directory, -1) != '/')
{
$directory .= '/';
}
// If directory is a directory, read in all the files
if (is_dir($directory))
{
$files = scandir($directory);
// Loop through said files, check for directories, and unlink files
foreach ($files as $file)
{
if (!in_array($file, array('.', '..')))
{
if (is_dir($directory . $file))
{
File::removeDirectory($directory . $file);
}
else
{
unlink($directory . $file);
}
}
}
}
rmdir($directory);
}
}
?>

682
classes/Form.php Normal file
View file

@ -0,0 +1,682 @@
<?php
/**
* Form Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Form Class
*
* This class contains methods for easily generating form elements. There is a
* heavy focus on select boxes as they have the most overhead for a developer.
*
* @deprecated
*/
class Form extends Object
{
// {{{ Get Instance
/**
* Get Instance
*
* Gets an instance of the Form class
*
* @static
* @param string $class name of the class to get an instance of
* @return object instance of the class
*/
public static function getInstance($class = 'Form')
{
return parent::getInstance($class);
}
// }}}
// {{{ Input
/**
* Input
*
* Generates an input with the passed data.
*
* @param string $name name (and ID) for the element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @param string $type optional type of input
* @param boolean $checked optional whether the input is checked
* @return string HTML for the input
*/
public function input($name, $value = null, $classes = null, $additional = null, $type = 'text', $checked = false)
{
if ($additional)
{
$additional = ' ' . $additional;
}
if (in_array($type, array('checkbox', 'radio')) && $checked == true)
{
$additional .= ' checked="checked"';
}
if ($value)
{
$additional .= ' value="' . $value . '"';
}
if ($classes)
{
$additional .= ' class="' . $classes . '"';
}
return '<input type="' . $type . '" name="' . $name . '" id="' . $name . '"' . $additional . ' />';
}
// }}}
// {{{ Hidden
/**
* Hidden
*
* Shorthand method to generate a hidden input.
*
* @param string $name name (and ID) for the element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function hidden($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'hidden');
}
/**
* Hidden Input
*
* Shorthand method to generate a hidden input.
*
* @deprecated Use hidden() instead
*
* @param string $name name (and ID) for the element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function hiddenInput($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'hidden');
}
// }}}
// {{{ Password
/**
* Password
*
* Shorthand method to generate a password input.
*
* @param string $name name (and ID) for the element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function password($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'password');
}
/**
* Password Input
*
* Shorthand method to generate a password input.
*
* @deprecated Use password() instead
*
* @param string $name name (and ID) for the element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function passwordInput($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'password');
}
// }}}
// {{{ Submit
/**
* Submit
*
* Shorthand method to generate a submit input (button).
*
* @param string $name name (and ID) for the input element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function submit($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'submit');
}
/**
* Submit Input
*
* Shorthand method to generate a submit input (button).
*
* @deprecated Use submit() instead
*
* @param string $name name (and ID) for the input element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function submitInput($name, $value = null, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'submit');
}
// }}}
// {{{ Security
/**
* Security
*
* Generates a hidden input with an SHA1 hash as the value. The name of the
* field is cannot be changed as this method was only intended for use with
* forms that are submitted via AJAX to provide better security.
*
* @param string $value value to hash
* @return string HTML for the input
*/
public function security($value)
{
// Returns the hidden input
return $this->hiddenInput('security_hash', Security::generateHash($value));
}
/**
* Security Input
*
* Generates a hidden input with an SHA1 hash as the value. The name of the
* field is cannot be changed as this method was only intended for use with
* forms that are submitted via AJAX to provide better security.
*
* @deprecated Use security() instead
*
* @param string $value value to hash
* @return string HTML for the input
*/
public function securityInput($value)
{
// Returns the hidden input
return $this->hiddenInput('security_hash', Security::generateHash($value));
}
// }}}
// {{{ Checkbox
/**
* Checkbox
*
* Generates a checkbox input with the passed data.
*
* @param string $name name (and ID) for the select element
* @param string $value optional preset value
* @param boolean $checked optional whether the checkbox is checked
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function checkbox($name, $value = null, $checked = false, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'checkbox', $checked);
}
// }}}
// {{{ Checkboxes
// @todo
// }}}
// {{{ Radio Button
/**
* Radio Button
*
* Generates a radio input with the passed data.
*
* @param string $name name (and ID) for the select element
* @param string $value optional preset value
* @param boolean $checked optional whether the checkbox is checked
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the input
*/
public function radio($name, $value = null, $checked = false, $classes = null, $additional = null)
{
return $this->input($name, $value, $classes, $additional, 'radio', $checked);
}
// }}}
// {{{ Radio Buttons
// @todo
// }}}
// {{{ Text Area
/**
* Textarea
*
* Generates a textarea with the passed data.
*
* @param string $name name (and ID) for the select element
* @param string $value optional preset value
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @param string $type optional type of input
* @return string HTML for the input
*/
public function textarea($name, $value = null, $classes = null, $additional = null)
{
if ($additional)
{
$additional = ' ' . $additional;
}
if ($classes)
{
$additional .= ' class="' . $classes . '"';
}
return '<textarea name="' . $name . '" id="' . $name . '"' . $additional . '>' . $value . '</textarea>';
}
// }}}
// {{{ Select
/**
* Select
*
* Generates a select box with the passed data.
*
* @param string $name name (and ID) for the select element
* @param array $options key/values for the option elements
* @param string $selected optional selected option
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the select box
*/
public function select($name, $options, $selected = null, $classes = null, $additional = null)
{
if ($additional)
{
$additional = ' ' . $additional;
}
if ($classes)
{
$additional .= ' class="' . $classes . '"';
}
return '<select id="' . $name . '" name="' . $name . '" class="' . $classes . '"' . $additional . '>' . $this->options($options, $selected) . '</select>';
}
// }}}
// {{{ Options
/**
* Options
*
* Generates the option elements from the passed array
*
* @param array $options key/values for the options
* @param string $selected optional default option
* @return string HTML for the options
*/
public function options($options, $selected = null)
{
$found_selected = false;
$options_html = '';
if (is_array($options))
{
foreach ($options as $main_key => $main_label)
{
if (is_array($main_label))
{
$options_html .= '<optgroup label="' . addslashes($main_key) . '">';
foreach ($main_label as $sub_key => $sub_label)
{
$selected_attribute = false;
if ($selected !== null && $found_selected === false)
{
if ($selected == $sub_key)
{
$selected_attribute = ' selected="selected"';
$found_selected = true;
}
}
$options_html .= '<option label="' . addslashes($sub_label) . '" value="' . $sub_key . '"' . $selected_attribute . '>' . $sub_label . '</option>';
}
$options_html .= '</optgroup>';
}
else
{
$selected_attribute = false;
if ($selected !== null && $found_selected === false)
{
if ($selected == $main_key)
{
$selected_attribute = ' selected="selected"';
$found_selected = true;
}
}
$options_html .= '<option label="' . addslashes($main_label) . '" value="' . $main_key . '"' . $selected_attribute . '>' . $main_label . '</option>';
}
}
}
if ($selected !== null && $found_selected === false)
{
$options_html .= '<option value="' . $selected . '" selected="selected" class="error">' . $selected . '</option>';
}
return $options_html;
}
// }}}
// {{{ State Select
/**
* State Select
*
* Generates a select box with the United States, Puerto Rico and miliary
* options
*
* @param string $name optional name (and ID) for the select element
* @param string $selected optional selected option
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the select box
*/
public function stateSelect($name = 'state', $selected = null, $classes = null, $additional = null)
{
$options = array(
null => '-- Select State --',
'AK' => 'Alaska',
'AL' => 'Alabama',
'AS' => 'American Samoa',
'AZ' => 'Arizona',
'AR' => 'Arkansas',
'CA' => 'California',
'CO' => 'Colorado',
'CT' => 'Connecticut',
'DE' => 'Delaware',
'DC' => 'District of Columbia',
'FL' => 'Florida',
'GA' => 'Georgia',
'GU' => 'Guam',
'HI' => 'Hawaii',
'ID' => 'Idaho',
'IL' => 'Illinois',
'IN' => 'Indiana',
'IA' => 'Iowa',
'KS' => 'Kansas',
'KY' => 'Kentucky',
'LA' => 'Louisiana',
'ME' => 'Maine',
'MH' => 'Marshall Islands',
'MD' => 'Maryland',
'MA' => 'Massachusetts',
'MI' => 'Michigan',
'MN' => 'Minnesota',
'MS' => 'Mississippi',
'MO' => 'Missouri',
'MT' => 'Montana',
'NE' => 'Nebraska',
'NV' => 'Nevada',
'NH' => 'New Hampshire',
'NJ' => 'New Jersey',
'NM' => 'New Mexico',
'NY' => 'New York',
'NC' => 'North Carolina',
'ND' => 'North Dakota',
'MP' => 'Northern Mariana Islands',
'OH' => 'Ohio',
'OK' => 'Oklahoma',
'OR' => 'Oregon',
'PW' => 'Palau',
'PA' => 'Pennsylvania',
'PR' => 'Puerto Rico',
'RI' => 'Rhode Island',
'SC' => 'South Carolina',
'SD' => 'South Dakota',
'TN' => 'Tennessee',
'TX' => 'Texas',
'UT' => 'Utah',
'VT' => 'Vermont',
'VI' => 'Virgin Islands',
'VA' => 'Virginia',
'WA' => 'Washington',
'WV' => 'West Virginia',
'WI' => 'Wisconsin',
'WY' => 'Wyoming',
'AE' => 'Armed Forces Africa',
'AA' => 'Armed Forces Americas (except Canada)',
'AE' => 'Armed Forces Canada',
'AE' => 'Armed Forces Europe',
'AE' => 'Armed Forces Middle East',
'AP' => 'Armed Forces Pacific'
);
return $this->select($name, $options, $selected, $classes, $additional);
}
// }}}
// {{{ Date Select
/**
* Date Select
*
* Generates 3 select boxes (month, day, year)
*
* @param string $name optional name (and ID) for the select element
* @param string $selected optional selected option
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @param integer $start_year optional first year to display
* @param integer $end_year optional last year to display
* @return string HTML for the select boxes
*/
public function dateSelect($name = 'date', $selected = null, $classes = null, $additional = null, $start_year = null, $end_year = null)
{
$html = '';
// Breaks apart the selected value if present
if ($selected == null || $selected == '0000-00-00')
{
$selected_month = null;
$selected_day = null;
$selected_year = null;
}
else
{
list($selected_year, $selected_month, $selected_day) = explode('-', $selected);
}
$month_options = array(
null => 'Month',
'01' => 'January',
'02' => 'February',
'03' => 'March',
'04' => 'April',
'05' => 'May',
'06' => 'June',
'07' => 'July',
'08' => 'August',
'09' => 'September',
'10' => 'October',
'11' => 'November',
'12' => 'December',
);
$day_options = array(null => 'Day');
$year_options = array(null => 'Year');
// Generates the list of days
for ($i = 1; $i <= 31; ++$i)
{
$day_options[str_pad($i, 2, '0', STR_PAD_LEFT)] = $i;
}
// Generates the list of years
$current_year = date('Y');
$start_year = $start_year == null ? $current_year - 10 : $start_year;
$end_year = $end_year == null ? $current_year + 10 : $end_year;
for ($i = $start_year; $i >= $end_year; --$i)
{
$year_options[$i] = $i;
}
// Loops through and generates the selects
foreach (array('month', 'day', 'year') as $part)
{
$options = $part . '_options';
$selected = 'selected_' . $part;
$html .= ' ' . $this->select($name . '[' . $part . ']', $$options, $$selected, $classes, $additional);
}
return $html;
}
// }}}
// {{{ Date of Birth Select
/**
* Date of Birth Select
*
* Generates 3 select boxes (month, day, year)
*
* @param string $name optional name (and ID) for the select element
* @param string $selected optional selected option
* @param string $classes optional class names
* @param string $additional optional additional parameters
* @return string HTML for the select boxes
*/
public function dobSelect($name = 'dob', $selected = null, $classes = null, $additional = null)
{
// Note: Start year based on oldest living person: http://en.wikipedia.org/wiki/Oldest_people as of November 2010
// Note: Start and end year may seem backwards, but we want them in descending order when rendered
return $this->dateSelect($name, $selected, $classes, $additional, date('Y'), 1896);
}
// }}}
// {{{ Polar Select
/**
* Polar Select
*
* Generates a polar (yes / no) select box.
*
* @param string $name optional name (and ID) for the select element
* @param string $selected optional selected option
* @param string $classes optional class names
* @param string $additional optional additional parameters
*/
public function polarSelect($name = 'decision', $selected = 0, $classes = null, $additional = null)
{
$options = array(1 => 'Yes', 0 => 'No');
return $this->select($name, $options, $selected, $classes, $additional);
}
// }}}
// {{{ Phone Input
/**
* Phone Input
*
* Generates 3 inputs for a phone number from the passed values.
*
* @param string $name optional name (and ID) for the input elements
* @param string $value optional existing value
* @param string $classes optional class names
* @param string $additional optional additional parameters
*/
public function phoneInput($name = 'phone', $value = null, $classes = null, $additional = null)
{
if ($value == null)
{
$value = array(
'area_code' => '',
'prefix' => '',
'line_number' => ''
);
}
else
{
$value = array(
'area_code' => substr($value, 0, 3),
'prefix' => substr($value, 3, 3),
'line_number' => substr($value, 6)
);
}
$parts = array(
'area_code' => 3,
'prefix' => 3,
'line_number' => 4
);
if ($additional)
{
$additional = ' ' . $additional;
}
$additional .= ' class="digits';
if ($classes)
{
$additional .= ' ' . $classes;
}
$additional .= '"';
$html = '';
foreach ($parts as $part => $size)
{
$html .= ($html != '' ? ' ' : '');
$html .= '<input type="input" name="' . $name . '[' . $part . ']" id="' . $name . '[' . $part . ']" value="' . $value[$part] . '" minlength="' . $size . '" maxlength="' . $size . '"' . $additional . ' />';
}
return $html;
}
// }}}
}
?>

155
classes/HTML.php Normal file
View file

@ -0,0 +1,155 @@
<?php
/**
* HTML Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* HTML Class
*
* This class contains methods for easily generating HTML elements.
*/
class HTML extends Object
{
private $self_closing = array('br', 'hr', 'img', 'input', 'link', 'meta');
public function __call($method, $arguments)
{
$attributes = null;
$contents = null;
if (isset($arguments[0]))
{
$attributes = $arguments[0];
}
if (isset($arguments[1]))
{
$contents = $arguments[1];
}
// ->inputType('name', $attributes);
if (preg_match('/^input/', $method) && !isset($attributes['label']))
{
$type = strtolower(str_replace('input', '', $method));
switch ($type)
{
case 'datetimelocal': $type = 'datetime-local'; break;
case '': $type = 'text'; break;
}
$method = 'input';
if (is_array($attributes))
{
$attributes['type'] = $type;
}
else
{
$attributes = array('type' => $type);
}
}
if (is_array($attributes) && isset($attributes['label']))
{
if (isset($attributes['name']))
{
$label = $this->label(array('for' => $attributes['name']), $attributes['label']);
unset($attributes['label']);
}
else
{
$label = $this->label($attributes['label']);
}
return $label . $this->$method($attributes, $contents);
}
else
{
return $this->element($method, $attributes, $contents);
}
}
// {{{ Get Instance
/**
* Get Instance
*
* Gets an instance of the Form class
*
* @static
* @param string $class name of the class to get an instance of
* @return object instance of the class
*/
public static function getInstance($class = 'HTML')
{
return parent::getInstance($class);
}
// }}}
public function element($element)
{
$attributes = null;
$contents = null;
foreach (func_get_args() as $key => $value)
{
if ($key && $key < 3)
{
if (is_array($value))
{
$attributes = $value;
}
elseif ($value)
{
$contents = $value;
}
}
}
$element = strtolower($element);
$html = '<' . $element;
if ($attributes)
{
if (is_array($attributes))
{
foreach ($attributes as $attribute => $value)
{
$html .= ' ' . $attribute . '="' . str_replace('"', '\"', $value) . '"';
}
}
else
{
throw new Exception('Attributes must be an array.');
}
}
if ($contents || !in_array($element, $this->self_closing))
{
$html .= '>' . $contents . '</' . $element . '>';
}
else
{
$html .= ' />';
}
return $html;
}
}
?>

152
classes/Log.php Normal file
View file

@ -0,0 +1,152 @@
<?php
/**
* Logging System for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Log Class
*
* Standardized logging methods for ease of reporting.
*/
class Log
{
/**
* Log Information
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function information($message)
{
return self::write('information', $message);
}
/**
* Log Warning
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function warning($message)
{
return self::write('warning', $message);
}
/**
* Log Error
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function error($message)
{
return self::write('error', $message);
}
/**
* Log Slow Query
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function slowQuery($message)
{
return self::write('slow_query', $message);
}
/**
* Log Credit Card Transaction
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function transaction($message)
{
return self::write('transaction', $message);
}
/**
* Log PHP Error
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function phpError($message, $time = false)
{
return self::write('php_error', $message, false, $time);
}
/**
* Log SQL Query
*
* @static
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
public static function query($message)
{
return self::write('query', $message);
}
/**
* Write Message to Log File
*
* @static
* @access private
* @param string $message message to log
* @return boolean whether or not the write was successful
*/
private static function write($log_type, $message, $format = true, $time = false)
{
$log_path = LOG_PATH . date('Y/m/d/', ($time == false ? time() : $time));
try
{
if (!file_exists($log_path))
{
mkdir($log_path, 0755, true);
}
$log_file = $log_path . $log_type . '.log';
$message .= "\n";
if ($format == true)
{
$backtrace = debug_backtrace();
rsort($backtrace);
$frame = $backtrace[strpos($backtrace[0]['file'], 'index.php') === false ? 0 : 1];
return file_put_contents($log_file, date('H:i:s') . ' ' . str_replace(getcwd(), '', $frame['file']) . ':' . $frame['line'] . ' ' . $message, FILE_APPEND);
}
else
{
return file_put_contents($log_file, $message, FILE_APPEND);
}
}
catch (ErrorException $exception)
{
return false;
}
}
}
?>

1523
classes/Model.php Normal file

File diff suppressed because it is too large Load diff

274
classes/Module.php Normal file
View file

@ -0,0 +1,274 @@
<?php
/**
* Module Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Module Class
*
* This is a parent class that all PICKLES modules should be extending. Each
* module can specify it's own meta data and whether or not a user must be
* properly authenticated to view the page. Currently any pages without a
* template are treated as pages being requested via AJAX and the return will
* be JSON encoded. In the future this may need to be changed out for logic
* that allows the requested module to specify what display type(s) it can use.
*/
class Module extends Object
{
/**
* Cache Object
*
* @access protected
* @var object
*/
protected $cache = null;
/**
* Database Object
*
* @access protected
* @var object
*/
protected $db = null;
/**
* Fluid or Fixed?
*
* @access protected
* @var boolean
*/
protected $fluid = false;
/**
* Page Title
*
* @access protected
* @var string, null by default
*/
protected $title = null;
/**
* Meta Description
*
* @access protected
* @var string, null by default
*/
protected $description = null;
/**
* Meta Keywords (comma separated)
*
* @access protected
* @var string, null by default
*/
protected $keywords = null;
/**
* Secure
*
* Whether or not the page should be loaded via SSL.
*
* @access protected
* @var boolean, null by default
*/
protected $secure = null;
/**
* Private
*
* Whether or not the page can be accessed directly.
*
* @access protected
* @var boolean, false by default
*/
protected $private = false;
/**
* Security Settings
*
* @access protected
* @var boolean, null by default
*/
protected $security = null;
/**
* Session
*
* Whether or not a session should be established.
*
* @access protected
* @var boolean, null by default
*/
protected $session = null;
/**
* Method
*
* Request methods that are allowed to access the module.
*
* @access protected
* @var string or array, null by default
*/
protected $method = null;
/**
* Hash
*
* Whether or not to validate the security hash. Boolean true will indicate
* using the name of the module as the hash, a string value will use the
* value instead.
*
* @access protected
* @var string or boolean, null by default
*/
protected $hash = null;
/**
* Default Display Engine
*
* Defaults to PHP but could be set to JSON, XML or RSS. Value is
* overwritten by the config value if not set by the module.
*
* @access protected
* @var string, null by default
*/
protected $engine = DISPLAY_PHP;
/**
* Default Template
*
* Defaults to null but could be set to any valid template basename. The
* value is overwritten by the config value if not set by the module. The
* display engine determines what the file extension should be.
*
* @access protected
* @var string, 'index' by default
*/
protected $template = 'index';
/**
* Return
*
* Array that is returned to the template in the case of the module not
* returning anything itself. This is somewhat of a one way trip as you
* cannot get the variable unless you reference the return array explicitly
* $this->return['variable']
*
* @access protected
* @var array
*/
protected $return = array();
/**
* Constructor
*
* The constructor does nothing by default but can be passed a boolean
* variable to tell it to automatically run the __default() method. This is
* typically used when a module is called outside of the scope of the
* controller (the registration page calls the login page in this manner.
*
* @param boolean $autorun optional flag to autorun __default()
*/
public function __construct($autorun = false)
{
parent::__construct();
$this->cache = Cache::getInstance();
$this->db = Database::getInstance();
if ($autorun === true)
{
$this->__default();
}
}
/**
* Default "Magic" Method
*
* This function is overloaded by the module. The __default() method is
* where you want to place any code that needs to be executed at runtime.
* The reason the code isn't in the constructor is because the module must
* be instantiated before the code is executed so that the controller
* script is aware of the authentication requirements.
*/
public function __default()
{
}
/**
* Magic Setter Method
*
* Places the variables that are being modified in the return array that is
* returned if nothing is returned by the module itself. This also prohibits
* the direct modification of module variables which could cause issues.
*
* @param string $name name of the variable to be set
* @param mixed $value value of the variable to be set
*/
public function __set($name, $value)
{
$this->return[$name] = $value;
}
/**
* Magic Getter Method
*
* Attempts to load the module variable. If it's not set, will attempt to
* load from the config.
*
* @param string $name name of the variable requested
* @return mixed value of the variable or boolean false
*/
public function __get($name)
{
if (!isset($this->$name))
{
if (isset($this->config->pickles[$name]))
{
$this->$name = $this->config->pickles[$name];
}
else
{
$this->$name = false;
}
}
return $this->$name;
}
/**
* Sets the Request
*
* @param array $request data to be loaded into the request variable
* @return boolean whether or not the assignment was successful
*/
public function setRequest($request)
{
$backtrace = debug_backtrace();
if ($backtrace[1]['class'] == 'Controller')
{
$this->request = $request;
return true;
}
else
{
throw new Exception('Only Controller can perform setRequest()');
}
}
}
?>

61
classes/Number.php Normal file
View file

@ -0,0 +1,61 @@
<?php
/**
* Number Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Number Class
*
* Just a simple collection of static functions to accomplish some of the more
* redundant numeric related manipulation.
*/
class Number
{
/**
* Ordinal Indiciator
*
* Formats a number by appending an ordinal indicator.
*
* @static
* @link http://en.wikipedia.org/wiki/Ordinal_indicator
* @link http://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers
* @param string $number number to format
* @param boolean $superscript include <sup> tags
* @return string formatted number
*/
public static function ordinalIndicator($number, $superscript = false)
{
$suffix = 'th';
if (!in_array(($number % 100), array(11, 12, 13)))
{
switch ($number % 10)
{
case 1: $suffix = 'st'; break;
case 2: $suffix = 'nd'; break;
case 3: $suffix = 'rd'; break;
}
}
if ($superscript)
{
$suffix = '<sup>' . $suffix . '</sup>';
}
return $number . $suffix;
}
}
?>

122
classes/Object.php Normal file
View file

@ -0,0 +1,122 @@
<?php
/**
* Object Class File for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Object Class
*
* Every instantiated class in PICKLES should be extending this class. By doing
* so the class is automatically hooked into the profiler, and the object will
* have access to some common components as well.
*/
class Object
{
/**
* Object Instances
*
* @static
* @access private
* @var mixed
*/
protected static $instances = array();
/**
* Instance of the Config object
*
* @access protected
* @var object
*/
protected $config = null;
/**
* Profiler flag
*
*
* @access private
* @var mixed
*/
private $profiler = false;
/**
* Constructor
*
* Establishes a Config instance for all children to enjoy
*/
public function __construct()
{
// Gets an instance of the config, unless we ARE the config
if (get_class($this) == 'Config')
{
$this->config = true;
}
else
{
$this->config = Config::getInstance();
}
// Assigns the profiler flag
$this->profiler = (isset($this->config->pickles['profiler']) && $this->config->pickles['profiler'] != '' ? $this->config->pickles['profiler'] : false);
// Optionally logs the constructor to the profiler
if ($this->profiler === true || ((is_array($this->profiler) && in_array('objects', $this->profiler)) || stripos($this->profiler, 'objects') !== false))
{
Profiler::log($this, '__construct');
}
}
/**
* Get Instance
*
* Gets an instance of the passed class. Allows for easy sharing of certain
* classes within the system to avoid the extra overhead of creating new
* objects each time. Also avoids the hassle of passing around variables.
*
* @static
* @param string $class name of the class
* @return object instance of the class
*/
public static function getInstance($class = false)
{
// In < 5.3 arguments must match in child, hence defaulting $class
if ($class == false)
{
return false;
}
else
{
if (!isset(self::$instances[$class]))
{
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
/**
* Destructor
*/
public function __destruct()
{
// Optionally logs the destructor to the profiler
if ($this->profiler === true || ((is_array($this->profiler) && in_array('objects', $this->profiler)) || stripos($this->profiler, 'objects') !== false))
{
Profiler::log($this, '__destruct');
}
}
}
?>

411
classes/Profiler.php Normal file
View file

@ -0,0 +1,411 @@
<?php
/**
* Profiler
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Profiler Class
*
* The Profiler class is statically interfaced with and allows for in depth
* profiling of a site. By default profiling is off, but can be enabled in the
* config.ini for a site. Out of the box the profiler will report on every
* class object in the system that extends the code Object class.
*
* Note: I really wanted to use PHP Quick Profiler by Ryan Campbell of
* Particletree but it kept barking out errors when I tried to use it with
* E_STRICT turned on. Here's a link anyway since it looks awesome:
* http://particletree.com/features/php-quick-profiler/
*
* @usage <code>Profiler::log('some action you want to track');</code>
* @usage <code>Profiler::log($object, 'methodName');</code>
*/
class Profiler
{
/**
* Config
*
* Profiler configuration
*
* @static
* @access private
* @var array
*/
private static $config;
/**
* Profile
*
* Array of logged events
*
* @static
* @access private
* @var array
*/
private static $profile = array();
/**
* Queries
*
* Number of queries that have been logged
*
* @static
* @access private
* @var integer
*/
private static $queries = 0;
/**
* Timers
*
* Array of active timers
*
* @static
* @access private
* @var array
*/
private static $timers = array();
/**
* Constructor
*
* Private constructor since this class is interfaced wtih statically.
*
* @access private
*/
private function __construct()
{
}
/**
* Enabled
*
* Checks if the profiler is set to boolean true or if the passed type is
* specified in the profiler configuration value.
*
* @param array $type type(s) to check
* @return boolean whether or not the type is enabled
*/
public static function enabled(/* polymorphic */)
{
// Grabs the config object if we don't have one yet
if (self::$config == null)
{
$config = Config::getInstance();
self::$config = $config->pickles['profiler'];
}
// Checks if we're set to boolean true
if (self::$config === true)
{
return true;
}
else
{
$types = func_get_args();
foreach ($types as $type)
{
if (stripos(self::$config, $type) !== false)
{
return true;
}
}
}
return false;
}
/**
* Log
*
* Logs the event to be displayed later on. Due to the nature of how much
* of a pain it is to determine which class method called this method I
* opted to make the method a passable argument for ease of use. Perhaps
* I'll revisit in the future. Handles all elapsed time calculations and
* memory usage.
*
* @static
* @param mixed $data data to log
* @param string $method name of the class method being logged
*/
public static function log($data, $method = false, $type = false)
{
$time = microtime(true);
$data_type = ($data == 'timer' ? $data : gettype($data));
// Tidys the data by type
switch ($data_type)
{
case 'array':
$log = '<pre>' . print_r($data, true) . '</pre>';
break;
case 'object':
$log = '<span style="color:#666">[</span><span style="color:#777">' . get_parent_class($data) . '</span><span style="color:#666">]</span> '
. '<span style="color:#69c">' . get_class($data) . '</span>'
. ($method != '' ? '<span style="color:#666">-></span><span style="color:#4eed9e">' . $method . '</span><span style="color:#666">()</span>' : '');
$data_type = '<span style="color:Peru">' . $data_type . '</span>';
break;
case 'timer':
$log = $method;
$data_type = '<span style="color:#6c0">' . $data_type . '</span>';
break;
case 'string':
default:
if ($type != false)
{
$data_type = $type;
}
$log = $data;
break;
}
self::$profile[] = array(
'log' => $log,
'type' => $data_type,
'time' => $time,
'elapsed' => $time - PICKLES_START_TIME,
'memory' => memory_get_usage(),
);
}
/**
* Log Query
*
* Serves as a wrapper to get query data to the log function
*
* @static
* @param string $query the query being executed
* @param array $input_parameters optional prepared statement data
* @param array $explain EXPLAIN data for the query
* @param float $duration the speed of the query
*/
public static function logQuery($query, $input_parameters = false, $explain = false, $duration = false)
{
self::$queries++;
$log = '';
if ($input_parameters != 'false' && is_array($input_parameters))
{
$log .= '<br />';
foreach ($input_parameters as $key => $value)
{
$log .= '<br /><span style="color:#a82222">' . $key . '</span> <span style="color:#666">=></span> <span style="color:#ffff7f">' . $value . '</span>';
$query = str_replace($key, '<span style="color:#a82222">' . $key . '</span>', $query);
}
}
$log = '<span style="color:#009600">' . $query . '</span>' . $log;
if (is_array($explain))
{
$log .= '<br />';
foreach ($explain as $table)
{
$log .= '<br /><span style="color:RoyalBlue">Possible Keys</span> <span style="color:#666">=></span> <span style="color:DarkGoldenRod">' . ($table['possible_keys'] == '' ? '<em style="color:red">NONE</em>' : $table['possible_keys']) . '</span>'
. '<br /><span style="color:RoyalBlue">Key</span> <span style="color:#666">=></span> <span style="color:DarkGoldenRod">' . ($table['key'] == '' ? '<em style="color:red">NONE</em>' : $table['key']) . '</span>'
. '<br /><span style="color:RoyalBlue">Type</span> <span style="color:#666">=></span> <span style="color:DarkGoldenRod">' . $table['type'] . '</span>'
. '<br /><span style="color:RoyalBlue">Rows</span> <span style="color:#666">=></span> <span style="color:DarkGoldenRod">' . $table['rows'] . '</span>'
. ($table['Extra'] != '' ? '<br /><span style="color:RoyalBlue">Extra</span> <span style="color:#666">=></span> <span style="color:DarkGoldenRod">' . $table['Extra'] . '</span>' : '');
}
}
$log .= '<br /><br /><span style="color:DarkKhaki">Speed:</span> ' . number_format($duration * 100, 3) . ' ms';
self::log($log, false, '<span style="color:DarkCyan">database</span>');
}
/**
* Timer
*
* Logs the start and end of a timer.
*
* @param string $timer name of the timer
* @return boolean whether or not timer profiling is enabled
*/
public static function timer($timer)
{
if (self::enabled('timers'))
{
// Starts the timer
if (!isset(self::$timers[$timer]))
{
self::$timers[$timer] = microtime(true);
self::Log('timer', '<span style="color:Orchid">Started timer</span> <span style="color:Yellow">' . $timer . '</span>');
}
// Ends the timer
else
{
self::Log('timer', '<span style="color:Orchid">Stopped timer</span> <span style="color:Yellow">' . $timer . '</span> <span style="color:#666">=></span> <span style="color:DarkKhaki">Time Elapsed:</span> ' . number_format((microtime(true) - self::$timers[$timer]) * 100, 3) . ' ms');
unset(self::$timers[$timer]);
}
return true;
}
return false;
}
/**
* Report
*
* Generates the Profiler report that is displayed by the Controller.
* Contains all the HTML needed to display the data properly inline on the
* page. Will generally be displayed after the closing HTML tag.
*/
public static function report()
{
?>
<style>
#pickles-profiler
{
background: #212121;
width: 800px;
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 20px;
-moz-box-shadow: 0 3px 4px rgba(0,0,0,0.5);
-webkit-box-shadow: 0 3px 4px rgba(0,0,0,0.5);
box-shadow: 0 3px 4px rgba(0,0,0,0.5);
border: 6px solid #666;
padding: 10px 20px 20px;
font-family: monospace;
font-size: 12px;
text-align: left;
}
#pickles-profiler table
{
width: 100%;
}
#pickles-profiler table tr th, #pickles-profiler table tr td
{
padding: 10px;
}
#pickles-profiler .even
{
background-color: #323232;
}
#pickles-profiler, #pickles-profiler table tr td, #pickles-profiler table tr th
{
color: #efefe8;
}
</style>
<div id="pickles-profiler">
<strong style="font-size:1.5em">PICKLES Profiler</strong><br /><br />
<?php
if (count(self::$profile) == 0)
{
echo '<em style="line-height:18px">There is nothing to profile. This often happens when the profiler configuration is set to either "queries" or "explains" and there are no database queries on the page (common on pages that only have a template). You may want to set the profiler to boolean true to ensure you get a profile of the page.</em>';
}
else
{
$start_time = PICKLES_START_TIME;
$peak_usage = self::formatSize(memory_get_peak_usage());
$end_time = self::$profile[count(self::$profile) - 1]['time']; // TODO
$duration = ($end_time - $start_time);
$logs = count(self::$profile);
$logs .= ' Log' . ($logs == 1 ? '' : 's');
$files = count(get_included_files());
$files .= ' File' . ($files == 1 ? '' : 's');
$queries = self::$queries . ' Quer'. (self::$queries == 1 ? 'y' : 'ies');
?>
<table style="border-collapse:separate;border-spacing:1px;border-radius:10px;text-shadow:1px 1px 1px #000">
<tr>
<td style="text-align:center;background:#480000">
<span style="font-weight:bold;">Console</span>
<div style="color:#ff7f7f;font-size:1.2em;padding-top:10px"><?php echo $logs; ?></div>
</td>
<td style="text-align:center;background:#552200">
<span style="font-weight:bold;">Load Time</span>
<div style="color:#ffa366;font-size:1.2em;padding-top:10px"><?php echo number_format($duration * 100, 3) . ' ms / ' . ini_get('max_execution_time'); ?></div>
</td>
<td style="text-align:center;background:#545500">
<span style="font-weight:bold;">Memory Usage</span>
<div style="color:#ffff6d;font-size:1.2em;padding-top:10px"><?php echo $peak_usage . ' / ' . ini_get('memory_limit'); ?></div>
</td>
<td style="text-align:center;background:#004200">
<span style="font-weight:bold;">Database</span>
<div style="color:#7dff7d;font-size:1.2em;padding-top:10px"><?php echo $queries; ?></div>
</td>
<td style="text-align:center;background:#000048">
<span style="font-weight:bold;">Includes</span>
<div style="color:#c4c4ff;font-size:1.2em;padding-top:10px"><?php echo $files; ?></div>
</td>
</tr>
</table>
<table>
<tr>
<th style="text-align:left" colspan="2">Console</th>
<th style="text-align:right">Memory</th>
<th style="text-align:right">Time</th>
</tr>
<?php
foreach (self::$profile as $key => $entry)
{
?>
<tr>
<td style="font-weight:bold;color:#999"><?php echo $entry['type']; ?></td>
<td><?php echo $entry['log']; ?></td>
<td style="text-align:right" nowrap="nowrap"><?php echo self::formatSize($entry['memory']); ?></td>
<td style="text-align:right" nowrap="nowrap"><?php echo number_format($entry['elapsed'] * 100, 3); ?> ms</td>
</tr>
<?php
}
?>
</table>
<?php
}
?>
</div>
<br /><br />
<?php
}
/**
* Format Size
*
* Formats the passed size into a human readable string
*
* @static
* @access private
* @param float $filesize size of the file
* @return string formatted number string
*/
private static function formatSize($filesize)
{
$units = array('bytes', 'kB', 'MB', 'GB');
return number_format(round($filesize / pow(1024, ($i = floor(log($filesize, 1024)))), 2), 2) . ' ' . $units[$i];
}
}
?>

436
classes/Security.php Normal file
View file

@ -0,0 +1,436 @@
<?php
/**
* Security System for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Security Class
*
* Collection of static methods for handling security within a website running
* on PICKLES. Requires sessions to be enabled.
*
* @usage <code>Security::login(10);</code>
* @usage <code>Security::isLevel(SECURITY_LEVEL_ADMIN);</code>
*/
class Security
{
/**
* Lookup Cache
*
* Used to minimize database lookups
*
* @static
* @access private
* @var array
*/
private static $cache = array();
/**
* Generate Hash
*
* Generates an SHA1 hash from the provided string. Salt optional.
*
* @param string $source value to hash
* @param mixed $salts optional salt or salts
* @return string SHA1 hash
*/
public static function generateHash($source, $salts = null)
{
// Determines which salt(s) to use
if ($salts == null)
{
$config = Config::getInstance();
if (isset($config->security['salt']) && $config->security['salt'] != null)
{
$salts = $config->security['salt'];
}
else
{
$salts = array('P1ck73', 'Ju1C3');
}
}
// Forces the variable to be an array
if (!is_array($salts))
{
$salts = array($salts);
}
// Loops through the salts, applies them and calculates the hash
$hash = $source;
foreach ($salts as $salt)
{
$hash = sha1($salt . $hash);
}
return $hash;
}
/**
* SHA-256
*
* Generates an SHA-256 hash from the provided string.
*
* @param string $source value to hash
* @return string SHA1 hash
*/
public static function sha256($source)
{
return hash('sha256', $source);
}
/**
* Generate SHA-256 Hash
*
* Generates an SHA-256 hash from the provided string and salt. Borrowed the
* large iteration logic from fCryptography::hashWithSalt() as, and I quote,
* "makes rainbow table attacks infesible".
*
* @param string $source value to hash
* @param mixed $salt value to use as salt
* @return string SHA-256 hash
* @link https://github.com/flourishlib/flourish-classes/blob/master/fCryptography.php
*/
public static function generateSHA256Hash($source, $salt)
{
$sha256 = sha1($salt . $source);
for ($i = 0; $i < 1000; $i++)
{
$sha256 = Security::sha256($sha256 . (($i % 2 == 0) ? $source : $salt));
}
return $sha256;
}
/**
* Check Session
*
* Checks if sessions are enabled.
*
* @static
* @access private
* @return boolean whether or not sessions are enabled
*/
private static function checkSession()
{
if (session_id() == '')
{
return false;
}
else
{
return true;
}
}
/**
* Check Level
*
* Checks if a passed level is an integer and/or properly defined in the
* site's configuration file.
*
* @static
* @access private
* @param mixed access level to validate
* @return whether ot not the access level is valid
*/
private static function checkLevel(&$access_level)
{
if (is_int($access_level))
{
return true;
}
else
{
$config = Config::getInstance();
// Attempts to validate the string passed
if (isset($config->security[$access_level]))
{
if (is_numeric($config->security[$access_level]))
{
$access_level = (int)$config->security[$access_level];
return true;
}
else
{
throw new Exception('Level "' . $access_level . '" is not numeric in config.ini');
}
}
else
{
throw new Exception('Level "' . $access_level . '" is not defined in config.ini');
}
}
return false;
}
/**
* Login
*
* Creates a session variable containing the user ID and generated token.
* The token is also assigned to a cookie to be used when validating the
* security level. When the level value is present, the class will by pass
* the database look up and simply use that value when validating (the less
* paranoid scenario).
*
* @static
* @param integer $user_id ID of the user that's been logged in
* @param integer $level optional level for the user being logged in
* @param string $role textual representation of the user's level
* @return boolean whether or not the login could be completed
*/
public static function login($user_id, $level = null, $role = null)
{
if (self::checkSession())
{
$token = sha1(microtime());
$_SESSION['__pickles']['security'] = array(
'token' => $token,
'user_id' => (int)$user_id,
'level' => $level,
'role' => $role,
);
setcookie('pickles_security_token', $token);
return true;
}
else
{
return false;
}
}
/**
* Logout
*
* Clears out the security information in the session and the cookie.
*
* @static
* @return boolean true
*/
public static function logout()
{
if (isset($_SESSION['__pickles']['security']))
{
$_SESSION['__pickles']['security'] = null;
unset($_SESSION['__pickles']['security']);
setcookie('pickles_security_token', '', time() - 3600);
}
return true;
}
/**
* Get User Level
*
* Looks up the user level in the database and caches it. Cache is used
* for any subsequent look ups for the user. Also validates the session
* variable against the cookie to ensure everything is legit. If the user
* level is set in the session, that value will take precedence.
*
* return integer user level or false
*/
private static function getUserLevel()
{
if (self::checkSession() == true && isset($_SESSION['__pickles']['security']['user_id']))
{
// Checks the session against the cookie
if (isset($_SESSION['__pickles']['security']['token'], $_COOKIE['pickles_security_token'])
&& $_SESSION['__pickles']['security']['token'] != $_COOKIE['pickles_security_token'])
{
Security::logout();
}
elseif (isset($_SESSION['__pickles']['security']['level']) && $_SESSION['__pickles']['security']['level'] != null)
{
return $_SESSION['__pickles']['security']['level'];
}
// Hits the database to determine the user's level
else
{
// Checks the session cache instead of hitting the database
if (isset($_SESSION['__pickles']['security']['user_id'], self::$cache[(int)$_SESSION['__pickles']['security']['user_id']]))
{
return self::$cache[(int)$_SESSION['__pickles']['security']['user_id']];
}
else
{
// Pulls the config and defaults where necessary
$config = Config::getInstance();
if ($config->security === false)
{
$config = array();
}
else
{
$config = $config->security;
}
$defaults = array('login' => 'login', 'model' => 'User', 'column' => 'level');
foreach ($defaults as $variable => $value)
{
if (!isset($config[$variable]))
{
$config[$variable] = $value;
}
}
// Uses the model to pull the user's access level
$class = $config['model'];
$model = new $class(array('fields' => $config['column'], 'conditions' => array('id' => (int)$_SESSION['__pickles']['security']['user_id'])));
if ($model->count() == 0)
{
Security::logout();
}
else
{
$constant = 'SECURITY_LEVEL_' . $model->record[$config['column']];
if (defined($constant))
{
$constant = constant($constant);
self::$cache[(int)$_SESSION['__pickles']['security']['user_id']] = $constant;
return $constant;
}
else
{
throw new Exception('Security level constant is not defined');
}
}
}
}
}
return false;
}
/**
* Is Level
*
* Checks the user's access level is exactly the passed level
*
* @static
* @param integer $access_level access level to be checked against
* @return boolean whether or not the user is that level
*/
public static function isLevel()
{
$is_level = false;
if (self::checkSession())
{
$arguments = func_get_args();
if (is_array($arguments[0]))
{
$arguments = $arguments[0];
}
foreach ($arguments as $access_level)
{
if (self::checkLevel($access_level))
{
if (self::getUserLevel() == $access_level)
{
$is_level = true;
break;
}
}
}
}
return $is_level;
}
/**
* Has Level
*
* Checks the user's access level against the passed level.
*
* @static
* @param integer $access_level access level to be checked against
* @return boolean whether or not the user has access
*/
public static function hasLevel()
{
$has_level = false;
if (self::checkSession())
{
$arguments = func_get_args();
if (is_array($arguments[0]))
{
$arguments = $arguments[0];
}
foreach ($arguments as $access_level)
{
if (self::checkLevel($access_level))
{
if (self::getUserLevel() >= $access_level)
{
$has_level = true;
break;
}
}
}
}
return $has_level;
}
/**
* Between Level
*
* Checks the user's access level against the passed range.
*
* @static
* @param integer $low access level to be checked against
* @param integer $high access level to be checked against
* @return boolean whether or not the user has access
*/
public static function betweenLevel($low, $high)
{
$between_level = false;
if (self::checkSession())
{
if (self::checkLevel($low) && self::checkLevel($high))
{
$user_level = self::getUserLevel();
if ($user_level >= $low && $user_level <= $high)
{
$between_level = true;
break;
}
}
}
return $between_level;
}
}
?>

345
classes/Session.php Normal file
View file

@ -0,0 +1,345 @@
<?php
/**
* Session Handling for PICKLES
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2013, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Session Class
*
* Provides session handling via database instead of the file based session
* handling built into PHP. Using this class requires an array to be
* defined in place of the boolean true/false (on/off). If simply array(),
* the datasource will default to the value in
* $config['pickles']['datasource'] and if the table will default to
* "sessions". The format is as follows:
*
* $config = array(
* 'pickles' => array(
* 'session' => array(
* 'datasource' => 'mysql',
* 'table' => 'sessions',
* )
* )
* );
*
* In addition to the configuration variables, a table in your database
* must be created. The [MySQL] table schema is as follows:
*
* CREATE TABLE sessions (
* id varchar(32) COLLATE utf8_unicode_ci NOT NULL,
* session text COLLATE utf8_unicode_ci NOT NULL,
* expires_at datetime NOT NULL,
* PRIMARY KEY (id),
* INDEX (expires_at)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
*
* Note: The reason for not using a model class was to avoid a naming
* conflict between the Session model and the Session class itself. This
* will eventually be resolved when I abandon full 5.x support and migrate
* to 5.3+ (assuming that ever happens).
*/
class Session extends Object
{
/**
* Handler
*
* What the session is being handled by.
*
* @access private
* @var string
*/
private $handler = false;
/**
* Accessed At
*
* The UNIX timestamp of when the page was accessed.
*
* @access private
* @var integer
*/
private $accessed_at = null;
/**
* Time to Live
*
* The number of seconds the session should remain active. Corresponds
* to the INI variable session.gc_maxlifetime
*
* @access private
* @var integer
*/
private $time_to_live = null;
/**
* Datasource
*
* Name of the datasource, defaults to whatever the default datasource
* is defined to in config.php
*
* @access private
* @var string
*/
private $datasource = null;
/**
* Table
*
* Name of the database table in the aforementioned datasource that
* holds the session data. The expected schema is defined above.
*
* @access private
* @var string
*/
private $table = null;
/**
* Database
*
* Our database object to interact with the aforementioned datasource
* and table. This object is shared with other PICKLES internals.
*
* @access private
* @var object
*/
private $db = null;
/**
* Constructor
*
* All of our set up logic for the session in contained here. This
* object is initially instantiated from pickles.php and the session
* callbacks are established here. All variables are driven from
* php.ini and/or the site config. Once configured, the session is
* started automatically.
*/
public function __construct()
{
if (!IS_CLI)
{
parent::__construct();
// Sets up our configuration variables
if (isset($this->config->pickles['session']))
{
$session = $this->config->pickles['session'];
$version = 1;
}
if (isset($this->config->pickles['sessions']))
{
$session = $this->config->pickles['sessions'];
$version = 2;
}
$datasources = $this->config->datasources;
$this->handler = 'files';
$datasource = false;
$table = 'sessions';
if (isset($datasources[$session]))
{
$datasource = $datasources[$session];
$this->handler = $datasource['type'];
if (isset($datasource['hostname'], $datasource['port']))
{
$host = 'tcp://' . $datasource['hostname'] . ':' . $datasource['port'];
}
}
switch ($this->handler)
{
case 'memcache':
ini_set('session.save_handler', 'memcache');
ini_set('session.save_path', $host . '?persistent=1&amp;weight=1&amp;timeout=1&amp;retry_interval=15');
break;
// @todo memcached
case 'mysql':
// Sets our access time and time to live
$this->accessed_at = time();
$this->time_to_live = ini_get('session.gc_maxlifetime');
$this->datasource = $datasource;
$this->table = $table;
// Gets a database instance
$this->db = Database::getInstance($this->datasource);
// Initializes the session
$this->initialize();
break;
case 'redis':
// Keep in mind that the database value is ignored by phpredis
$save_path = $host . '?weight=1'
. (isset($datasource['database']) ? '&database=' . $datasource['database'] : '')
. (isset($datasource['prefix']) ? '&prefix=' . $datasource['prefix'] : '');
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', $save_path);
break;
default:
case 'files':
ini_set('session.save_handler', 'files');
break;
}
if (isset($_SERVER['HTTP_USER_AGENT'])
&& !String::isEmpty($_SERVER['HTTP_USER_AGENT'])
&& !preg_match('/(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)/i', $_SERVER['HTTP_USER_AGENT']))
{
session_start();
}
}
}
/**
* Destructor
*
* Runs garbage collection and closes the session. I'm not sure if the
* garbage collection should stay as it could be accomplished via
* php.ini variables. The session_write_close() is present to combat a
* chicken and egg scenario in earlier versions of PHP 5.
*/
public function __destruct()
{
if ($this->handler == 'mysql')
{
$this->gc($this->time_to_live);
session_write_close();
}
}
/**
* Initializes the Session
*
* This method exists to combat the fact that calling session_destroy()
* also clears out the save handler. Upon destorying a session this
* method is called again so the save handler is all set.
*/
public function initialize()
{
// Sets up the session handler
session_set_save_handler(
array($this, 'open'),
array($this, 'close'),
array($this, 'read'),
array($this, 'write'),
array($this, 'destroy'),
array($this, 'gc')
);
register_shutdown_function('session_write_close');
}
/**
* Opens the Session
*
* Since the session is in the database, opens the database connection.
* This step isn't really necessary as the Database object is smart
* enough to open itself up upon execute.
*/
public function open()
{
session_regenerate_id();
return $this->db->open();
}
/**
* Closes the Session
*
* Same as above, but in reverse.
*/
public function close()
{
return $this->db->close();
}
/**
* Reads the Session
*
* Checks the database for the session ID and returns the session data.
*
* @param string $id session ID
* @return string serialized session data
*/
public function read($id)
{
$sql = 'SELECT session FROM `' . $this->table . '` WHERE id = ?;';
$session = $this->db->fetch($sql, array($id));
return isset($session[0]['session']) ? $session[0]['session'] : '';
}
/**
* Writes the Session
*
* When there's changes to the session, writes the data to the
* database.
*
* @param string $id session ID
* @param string $session serialized session data
* @return boolean whether the query executed correctly
*/
public function write($id, $session)
{
$sql = 'REPLACE INTO `' . $this->table . '` VALUES (?, ? ,?);';
$parameters = array($id, $session, date('Y-m-d H:i:s', strtotime('+' . $this->time_to_live . ' seconds')));
return $this->db->execute($sql, $parameters);
}
/**
* Destroys the Session
*
* Deletes the session from the database.
*
* @param string $id session ID
* @return boolean whether the query executed correctly
*/
public function destroy($id)
{
$sql = 'DELETE FROM `' . $this->table . '` WHERE id = ?;';
return $this->db->execute($sql, array($id));
}
/**
* Garbage Collector
*
* This is who you call when you got trash to be taken out.
*
* @param integer $time_to_live number of seconds a session is active
* @return boolean whether the query executed correctly
*/
public function gc($time_to_live)
{
$sql = 'DELETE FROM `' . $this->table . '` WHERE expires_at < ?;';
$parameters = array(date('Y-m-d H:i:s', $this->accessed_at - $time_to_live));
return $this->db->execute($sql, $parameters);
}
}
?>

267
classes/String.php Normal file
View file

@ -0,0 +1,267 @@
<?php
/**
* String Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* String Class
*
* Just a simple collection of static functions to accomplish some of the
* more redundant string related manipulation.
*/
class String
{
// {{{ Format Phone Number
/**
* Format Phone Number
*
* Formats a 10 digit phone number with dashes as ###-###-####.
*
* @static
* @param string $number number to format
* @param string $replacement output of the string
* @return string formatted phone number
*/
public static function formatPhoneNumber($number, $replacement = '$1-$2-$3')
{
// Strips characters we don't need
$number = str_replace(array('(', ')', ' ', '-', '.', '_'), '', $number);
// Formats the number
return preg_replace('/^(\d{3})(\d{3})(.+)$/', $replacement, $number);
}
// }}}
// {{{ Generate Gravatar Hash
/**
* Generate Gravatar Hash
*
* Generates a hash from the passed string that can then be used for
* fetching an avatar from Gravatar.com
*
* @deprecated
* @static
* @param string $string string to hash, should be an email address
* @return string resulting hash
*/
public static function generateGravatarHash($string)
{
return API_Gravatar::hash($string);
}
// }}}
// {{{ Generate Slug
/**
* Generate Slug
*
* Generates a slug from the pass string by lowercasing the string,
* trimming whitespace and converting non-alphanumeric values to
* dashes. Takes care of multiple dashes as well.
*
* @static
* @param string $string to be converted to the slug
* @return string resulting slug
*/
public static function generateSlug($string)
{
$string = strtolower(trim($string));
$string = preg_replace('/[^a-z0-9-]/', '-', $string);
$string = preg_replace('/-+/', '-', $string);
return trim($string, '-');;
}
// }}}
// {{{ Is Empty
/**
* Is Empty
*
* Checks if a string is empty. You can use the PHP function empty()
* but that returns true for a string of "0". Last I checked, that's
* not an empty string. PHP's function also doesn't apply trim() to the
* value to ensure it's not just a bunch of spaces.
*
* @static
* @param string $value string(s) to be checked
* @return boolean whether or not the string is empty
*/
public static function isEmpty()
{
foreach (func_get_args() as $value)
{
if (trim($value) == '')
{
return true;
}
}
return false;
}
// }}}
// {{{ Pluralize
/**
* Pluralize
*
* Based on a passed integer, the word will be pluralized. A value of
* zero will also pluralize the word (e.g. 0 things not 0 thing).
*
* @static
* @param string $string the word to plurailze
* @param integer $count the count to interrogate
* @param boolean $both (optional) include count in return
* @return string pluralized word
*/
public static function pluralize($string, $count, $both = false)
{
if ($count != 1)
{
$string .= 's';
}
if ($both)
{
$string = $count . ' ' . $string;
}
return $string;
}
// }}}
// {{{ Random
/**
* Random
*
* Generates a pseudo-random string based on the passed parameters.
*
* Note: Similar characters = 0, O, 1, I (and may be expanded)
*
* @static
* @param integer $length optional length of the generated string
* @param boolean $alpha optional include alpha characters
* @param boolean $numeric optional include numeric characters
* @param boolean $similar optional include similar characters
* @return string generated string
*/
public static function random($length = 8, $alpha = true, $numeric = true, $similar = true)
{
$characters = array();
$string = '';
// Adds alpha characters to the list
if ($alpha == true)
{
if ($similar == true)
{
$characters = array_merge($characters, range('a', 'z'));
}
else
{
$characters = array_merge($characters, range('a', 'h'), range('j', 'n'), range('p', 'z'));
}
}
// Adds numeric characters to the list
if ($numeric == true)
{
if ($similar == true)
{
$characters = array_merge($characters, range('0', '9'));
}
else
{
$characters = array_merge($characters, range('2', '9'));
}
}
if (count($characters) > 0)
{
shuffle($characters);
for ($i = 0; $i < $length; $i++)
{
$string .= $characters[array_rand($characters)];
}
}
return $string;
}
// }}}
// {{{ Truncate
/**
* Truncate
*
* Truncates a string to a specified length and (optionally) adds a
* span to provide a rollover to see the expanded text.
*
* @static
* @param string $string string to truncate
* @param integer $length length to truncate to
* @param boolean $hover (optional) whether or not to add the rollover
* @return string truncate string
*/
public static function truncate($string, $length, $hover = true)
{
if (strlen($string) > $length)
{
if ($hover == true)
{
$string = '<span title="' . $string . '">' . substr($string, 0, $length) . '&hellip;</span>';
}
else
{
$string = substr($string, 0, $length) . '...';
}
}
return $string;
}
// }}}
// {{{ Upper Words
/**
* Upper Words
*
* Applies strtolower() and ucwords() to the passed string. The
* exception being email addresses which are not formatted at all.
*
* @static
* @param string $string string to format
* @return string formatted string
*/
public static function upperWords($string)
{
// Only formats non-email addresses
if (filter_var($string, FILTER_VALIDATE_EMAIL) == false)
{
$string = ucwords(strtolower($string));
}
return $string;
}
// }}}
}
?>

234
classes/Time.php Normal file
View file

@ -0,0 +1,234 @@
<?php
/**
* Time Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Time Class
*
* Just a simple collection of static functions to accomplish some of the more
* redundant time and date related manipulation.
*/
class Time
{
// {{{ Intervals (in seconds)
/**
* Minute
*
* Seconds in a minute
*
* @var integer
*/
const MINUTE = 60;
/**
* Hour
*
* Seconds in an hour (minute * 60)
*
* @var integer
*/
const HOUR = 3600;
/**
* Day
*
* Seconds in a day (hour * 24)
*
* @var integer
*/
const DAY = 86400;
/**
* Week
*
* Seconds in a week (day * 7)
*
* @var integer
*/
const WEEK = 604800;
/**
* Month
*
* Seconds in a month (day * 30)
*
* @var integer
*/
const MONTH = 2592000;
/**
* Quarter
*
* Seconds in a quarter (day * 90)
*
* @var integer
*/
const QUARTER = 7776000;
/**
* Year
*
* Seconds in a year (day * 365)
*
* @var integer
*/
const YEAR = 31536000;
/**
* Decade
*
* Seconds in a decade (year * 10)
*
* @var integer
*/
const DECADE = 315360000;
/**
* Century
*
* Seconds in a decade (decade * 10)
*
* @var integer
*/
const CENTURY = 3153600000;
// }}}
/**
* Age
*
* Calculates age based on the passed date.
*
* @static
* @param string $date birth / inception date
* @return integer $age number of years old
* @todo Wondering if this really should live in the Date class since it's a Date function. Could flip the aliasing to preserve any older code.
*/
public static function age($date)
{
if (!preg_match('/\d{4}-\d{2}-\d{2}/', $date))
{
$date = date('Y-m-d', strtotime($date));
}
list($year, $month, $day) = explode('-', $date, 3);
$age = date('Y') - $year;
if (date('md') < $month . $day)
{
$age--;
}
return $age;
}
/**
* Ago
*
* Generates a relative time (e.g. X minutes ago).
*
* @static
* @param mixed $time timestamp to calculate from
* @return string relative time
*/
public static function ago($time)
{
$current = strtotime(Time::timestamp());
$time = preg_match('/^\d+$/', $time) ? $time : strtotime($time);
if ($current == $time)
{
$time_ago = 'just now';
}
else
{
if ($current > $time)
{
$difference = $current - $time;
$suffix = ' ago';
}
else
{
$difference = $time - $current;
$suffix = ' from now';
}
// Less than 1 minute ago (seconds ago)
if ($difference < 60)
{
$time_ago = 'seconds';
}
// Less than 1 hour ago (minutes ago)
elseif ($difference < 3600)
{
$minutes = round($difference / 60);
$time_ago = $minutes . ' minute' . ($minutes != 1 ? 's' : '');
}
// Less than 1 day ago (hours ago)
elseif ($difference < 86400)
{
$hours = round($difference / 3600);
$time_ago = $hours . ' hour' . ($hours != 1 ? 's' : '');
}
// Less than 1 week ago (days ago)
elseif ($difference < 604800)
{
$days = round($difference / 86400);
$time_ago = $days . ' day' . ($days != 1 ? 's' : '');
}
// Less than 1 month ago (weeks ago)
elseif ($difference < 2419200)
{
$weeks = round($difference / 604800);
$time_ago = $weeks . ' week' . ($weeks != 1 ? 's' : '');
}
// Less than 1 year ago (months ago)
elseif ($difference < 31449600)
{
$months = round($difference / 2419200);
$time_ago = $months . ' month' . ($months != 1 ? 's' : '');
}
// Over 1 year ago (years ago)
else
{
$years = round($difference / 31449600);
$time_ago = $years . ' year' . ($years != 1 ? 's' : '');
}
$time_ago .= $suffix;
}
return $time_ago;
}
/**
* Timestamp
*
* Current Universal Time in the specified format.
*
* @static
* @param string $format format of the timestamp
* @return string $timestamp formatted timestamp
*/
public static function timestamp($format = 'Y-m-d H:i:s')
{
return gmdate($format);
}
}
?>

View file

@ -1,37 +0,0 @@
{
"name": "joshtronic/pickles",
"description": "Pickles is a PHP framework for building kick-ass services",
"type": "library",
"keywords": ["framework", "api", "soa", "oauth"],
"homepage": "http://picklesphp.com",
"license": "MIT",
"authors": [
{
"name": "Josh Sherman",
"email": "josh@gravityblvd.com",
"homepage": "http://joshtronic.com"
}
],
"support": {
"issues": "https://github.com/joshtronic/pickles/issues",
"source": "https://github.com/joshtronic/pickles"
},
"minimum-stability" : "dev",
"require-dev": {
"phpunit/phpunit": "dev-master",
"satooshi/php-coveralls": "dev-master"
},
"require": {
"php": ">=5.4",
"league/oauth2-server": "4.0.x-dev"
},
"suggest": {
"mongodb/mongo-php-driver": "Required to use the Mongo storage engine",
"predis/predis": "Required to use the Redis storage engine"
},
"autoload": {
"psr-4": {
"Pickles\\" : "src/"
}
}
}

1446
composer.lock generated

File diff suppressed because it is too large Load diff

8263
jar.php Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.2/phpunit.xsd"
bootstrap="tests/bootstrap.php"
colors="true"
stderr="true"
>
<testsuites>
<testsuite name="Pickles Test Harness">
<directory>./tests/Pickles</directory>
</testsuite>
</testsuites>
</phpunit>

417
pickles.php Normal file
View file

@ -0,0 +1,417 @@
<?php
/**
* Core PICKLES Include File
*
* This is the file that you include on the page you're instantiating the
* controller from (typically index.php). The path to the PICKLES code
* base is established as well as the path that Smarty will use to store
* the compiled pages.
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
* @usage <code>require_once 'pickles.php';</code>
*/
// Set to true while working on PICKLES to pack the JAR
// Assumption, OSX is considered a development platform
$pickling = (php_uname('s') == 'Darwin' ? true : false);
// {{{ PICKLES Constants
// Grabs the start time in case we're profiling
define('PICKLES_START_TIME', microtime(true));
// Establishes our PICKLES paths
define('PICKLES_PATH', dirname(__FILE__) . '/');
define('PICKLES_CLASS_PATH', PICKLES_PATH . 'classes/');
// Establishes our site paths
define('SITE_PATH', getcwd() . '/../');
define('SITE_CLASS_PATH', SITE_PATH . 'classes/');
define('SITE_MODEL_PATH', SITE_PATH . 'models/');
define('SITE_MODULE_PATH', SITE_PATH . 'modules/');
define('SITE_TEMPLATE_PATH', SITE_PATH . 'templates/');
define('PRIVATE_PATH', SITE_PATH . 'private/');
define('LOG_PATH', PRIVATE_PATH . 'logs/');
// Sets up constants for the Display names
define('DISPLAY_JSON', 'JSON');
define('DISPLAY_PHP', 'PHP');
define('DISPLAY_RSS', 'RSS');
define('DISPLAY_XML', 'XML');
// Creates a constant as to whether or not we have JSON available
define('JSON_AVAILABLE', function_exists('json_encode'));
// Creates a variable to flag if we're on the command line
define('IS_CLI', !isset($_SERVER['REQUEST_METHOD']));
// }}}
// {{{ Attempts to JAR the PICKLES - Har Har.
function readFileContents($directory)
{
$contents = '';
$files = scandir($directory);
foreach ($files as $file)
{
if (strpos($file, '.') !== 0)
{
$file = $directory . '/' . $file;
if (is_dir($file))
{
$contents .= readFileContents($file);
}
else
{
$contents .= file_get_contents($file);
}
}
}
return $contents;
}
$jar_file = PICKLES_PATH . 'jar.php';
if (is_writable(PICKLES_PATH) && $pickling)
{
file_put_contents(dirname(__FILE__) . '/jar.php', str_replace("\n?" . ">\n<" . "?php\n", '', readFileContents(PICKLES_CLASS_PATH)));
}
if (file_exists($jar_file) && !$pickling)
{
require $jar_file;
}
// }}}
// {{{ Defaults some important configuration options
// Turns on error before the config is loaded to help catch parse errors
ini_set('display_errors', true);
error_reporting(-1);
// Sets the error and exception handlers
// set_error_handler('__handleError');
// set_exception_handler('__handleException');
// Defaults timezone to UTC if not set
if (ini_get('date.timezone') == '')
{
ini_set('date.timezone', 'Etc/UTC');
}
// Sets the session variables
ini_set('session.cache_expire', 86400);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.entropy_length', 512);
ini_set('session.gc_maxlifetime', 86400);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 1000);
ini_set('session.hash_function', 1);
// }}}
// {{{ Loads the configuration file and sets any configuration options
// Loads the base config
$config = Config::getInstance();
// Configures any available PHP configuration options
if (is_array($config->php) && count($config->php) > 0)
{
foreach ($config->php as $variable => $value)
{
ini_set($variable, $value);
}
}
// Starts session handling (old)
if (isset($config->pickles['session']))
{
if (session_id() == '' && $config->pickles['session'] !== false)
{
new Session();
}
}
// Starts session handling (new)
if (isset($config->pickles['sessions']))
{
if (session_id() == '' && $config->pickles['sessions'] !== false)
{
new Session();
}
}
// }}}
// {{{ Defaults some internals for ease of use
if (!isset($_REQUEST['request']))
{
$_REQUEST['request'] = isset($config->pickles['module']) ? $config->pickles['module'] : '';
}
// }}}
// {{{ Auto[magical] Loader
/**
* Magic function to automatically load classes
*
* Attempts to load a core PICKLES class or a site level data model or
* module class. If Smarty is being requested, will load the proper class
* from the vendors directory
*
* @param string $class Name of the class to be loaded
* @return boolean Return value of require_once() or false (default)
*/
function __autoload($class)
{
$loaded = false;
$filename = preg_replace('/_/', '/', $class) . '.php';
// Path as the key, boolean value is whether ot not to convert back to hyphenated
$paths = array(
PICKLES_CLASS_PATH => false,
SITE_CLASS_PATH => false,
SITE_MODEL_PATH => false,
SITE_MODULE_PATH => true,
);
foreach ($paths as $path => $hyphenated)
{
// Converts the filename back to hypenated
if ($hyphenated == true)
{
$filename = strtolower(preg_replace('/([A-Z]{1})/', '-$1', $filename));;
}
if (file_exists($path . $filename))
{
$loaded = require_once $path . $filename;
break;
}
}
return $loaded;
}
// }}}
// {{{ Error Handler
/**
* Error handling function that thinks it's magical
*
* Catches errors (warnings and the like) and throws it back out as an
* ErrorException. This really helps trapping complex errors that need a ton of
* sanity checks, just try / catch and you're good. Also, this isn't a magic
* function, but I opted to use the __ prefix to help avoid a naming collision
* since namespace support is 5.3+ and PICKLES strives to be 5.0+ compatible.
*
* Keep in mind that fatal errors cannot and will not be handled.
*
* @param integer $errno the level of the error raised
* @param string $errstr the error message
* @param string $errfile filename that the error was raised in
* @param integer $errline line number the error was raised at
* @param array $errcontext array of every variable that existed in scope
* @return ErrorException not really returned, but worth documenting
*/
function __handleError($errno, $errstr, $errfile, $errline, array $errcontext)
{
// Handle hacktastic @ error suppression. Seriously, don't ever use @
if (error_reporting() === 0)
{
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
// }}}
// {{{ Exception Handler
/**
* Top level exception handling function
*
* Catches uncaught exceptions and displays them.
*
* @param object $exception the exception
*/
function __handleException($exception)
{
if (IS_CLI == true)
{
$lines = array();
$maxes = array('key' => 0, 'method' => 0, 'file' => 0, 'line' => 4);
$trace = $exception->getTrace();
rsort($trace);
foreach ($trace as $key => $data)
{
$method = '';
if (isset($data['class']))
{
$method .= $data['class'] . $data['type'];
}
$method .= $data['function'] . '()';
$line = array(
'key' => $key + 1 . '.',
'method' => $method,
'file' => (isset($data['file']) ? $data['file'] : __FILE__),
'line' => (isset($data['line']) ? $data['line'] : '0')
);
foreach (array_keys($maxes) as $variable)
{
$length = strlen($line[$variable]);
if ($length > $maxes[$variable])
{
$maxes[$variable] = $length;
}
}
$lines[] = $line;
}
$max_length = array_sum($maxes) + 11;
$horizontal_border = '+' . str_repeat('-', $max_length) . '+' . "\n";
echo $horizontal_border;
echo '|' . str_pad('Uncaught Exception', $max_length, ' ', STR_PAD_BOTH) . '|' . "\n";
echo $horizontal_border;
echo '|' . str_pad(' ' . $exception->getMessage(), $max_length) . '|' . "\n";
echo '|' . str_pad(' in ' . $exception->getFile() . ' on line ' . $exception->getLine(), $max_length) . '|' . "\n";
echo $horizontal_border;
echo '| ' . str_pad('Trace', $maxes['key'] + $maxes['method'] + 3) . ' | ' . str_pad('File', $maxes['file']) . ' | ' . str_pad('Line', $maxes['line']) . ' |' . "\n";
echo $horizontal_border;
foreach ($lines as $line)
{
echo '| ';
echo implode(
array(
str_pad($line['key'], $maxes['key'], ' ', STR_PAD_LEFT),
str_pad($line['method'], $maxes['method']),
str_pad($line['file'], $maxes['file']),
str_pad($line['line'], $maxes['line'], ' ', STR_PAD_LEFT)
),
' | '
);
echo ' |' . "\n";
}
echo $horizontal_border;
}
else
{
?>
<style>
#pickles-exception
{
background: #212121;
width: 800px;
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
border-radius: 20px;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
box-shadow: 0 3px 4px #000;
-moz-box-shadow: 0 3px 4px #000;
-webkit-box-shadow: 0 3px 4px #000;
border: 6px solid #666;
padding: 10px 20px 20px;
font-family: monospace;
font-size: 12px;
text-align: left;
}
#pickles-exception table
{
width: 100%;
}
#pickles-exception table tr th, #pickles-exception table tr td
{
padding: 10px;
}
#pickles-exception .even
{
background-color: #323232;
}
#pickles-exception, #pickles-exception table tr td, #pickles-exception table tr th
{
color: #efefe8;
}
</style>
<div id="pickles-exception">
<strong style="font-size:1.5em">Uncaught Exception</strong><br /><br />
<table style="border-collapse:separate;border-spacing:1px;border-radius:10px;text-shadow:1px 1px 1px #000;text-align:center">
<tr><td style="background-color:#480000;padding:10px">
<div style="font-size:1.5em;font-style:italic"><?php echo $exception->getMessage(); ?></div>
</td></tr>
<tr><td style="background-color:#552200;padding:10px">
<div style="font-size:1.2em"><?php echo $exception->getFile(); ?> on line <?php echo $exception->getLine(); ?></div>
</td></tr>
</table>
<table>
<tr>
<th style="text-align:left" colspan="2">Trace</th>
<th style="text-align:left">File</th>
<th style="text-align:right">Line</th>
</tr>
<?php
$trace = $exception->getTrace();
rsort($trace);
foreach ($trace as $key => $data)
{
$method = '';
if (isset($data['class']))
{
$method .= $data['class'] . $data['type'];
}
$method .= $data['function'] . '()';
?>
<tr>
<td style="font-weight:bold;color:#999"><?php echo $key + 1; ?>.</td>
<td><?php echo $method; ?></td>
<td><?php echo isset($data['file']) ? $data['file'] : __FILE__; ?></td>
<td style="text-align:right"><?php echo isset($data['line']) ? $data['line'] : '0'; ?></td>
</tr>
<?php
}
?>
</table>
</div>
<br /><br />
<?php
}
}
// }}}
?>

View file

@ -1,129 +0,0 @@
CREATE TABLE `oauth_clients` (
`id` CHAR(40) NOT NULL,
`secret` CHAR(40) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`auto_approve` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `u_oacl_clse_clid` (`secret`,`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_endpoints` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`client_id` char(40) NOT NULL,
`redirect_uri` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `i_oaclen_clid` (`client_id`),
CONSTRAINT `f_oaclen_clid`
FOREIGN KEY (`client_id`)
REFERENCES `oauth_clients` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_sessions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`client_id` char(40) NOT NULL,
`owner_type` enum('user','client') NOT NULL DEFAULT 'user',
`owner_id` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `i_uase_clid_owty_owid` (`client_id`,`owner_type`,`owner_id`),
CONSTRAINT `f_oase_clid`
FOREIGN KEY (`client_id`)
REFERENCES `oauth_clients` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_access_tokens` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`session_id` int(10) unsigned NOT NULL,
`access_token` char(40) NOT NULL,
`expires_at` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u_oaseacto_acto_seid` (`access_token`,`session_id`),
KEY `f_oaseto_seid` (`session_id`),
CONSTRAINT `f_oaseto_seid`
FOREIGN KEY (`session_id`)
REFERENCES `oauth_sessions` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_authorization_codes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`session_id` int(10) unsigned NOT NULL,
`authorization_code` char(40) NOT NULL,
`expires_at` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `session_id` (`session_id`),
CONSTRAINT `oauth_authorization_codes_ibfk_1`
FOREIGN KEY (`session_id`)
REFERENCES `oauth_sessions` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_redirect_uris` (
`session_id` int(10) unsigned NOT NULL,
`redirect_uri` varchar(255) NOT NULL,
PRIMARY KEY (`session_id`),
CONSTRAINT `f_oasere_seid`
FOREIGN KEY (`session_id`)
REFERENCES `oauth_sessions` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_refresh_tokens` (
`access_token_id` int(10) unsigned NOT NULL,
`refresh_token` char(40) NOT NULL,
`expires_at` int(10) unsigned NOT NULL,
`client_id` char(40) NOT NULL,
PRIMARY KEY (`access_token_id`),
KEY `client_id` (`client_id`),
CONSTRAINT `oauth_refresh_tokens_ibfk_1`
FOREIGN KEY (`client_id`)
REFERENCES `oauth_clients` (`id`)
ON DELETE CASCADE,
CONSTRAINT `f_oasetore_setoid`
FOREIGN KEY (`access_token_id`)
REFERENCES `oauth_access_tokens` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_scopes` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`scope` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u_oasc_sc` (`scope`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_access_token_scopes` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`access_token_id` int(10) unsigned DEFAULT NULL,
`scope_id` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u_setosc_setoid_scid` (`access_token_id`,`scope_id`),
KEY `f_oasetosc_scid` (`scope_id`),
CONSTRAINT `f_oasetosc_scid`
FOREIGN KEY (`scope_id`)
REFERENCES `oauth_scopes` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `f_oasetosc_setoid`
FOREIGN KEY (`access_token_id`)
REFERENCES `oauth_access_tokens` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
CREATE TABLE `oauth_authorization_code_scopes` (
`authorization_code_id` int(10) unsigned NOT NULL,
`scope_id` smallint(5) unsigned NOT NULL,
KEY `authorization_code_id` (`authorization_code_id`),
KEY `scope_id` (`scope_id`),
CONSTRAINT `oauth_authorization_code_scopes_ibfk_2`
FOREIGN KEY (`scope_id`)
REFERENCES `oauth_scopes` (`id`)
ON DELETE CASCADE,
CONSTRAINT `oauth_authorization_code_scopes_ibfk_1`
FOREIGN KEY (`authorization_code_id`)
REFERENCES `oauth_authorization_codes` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;

View file

@ -1,212 +0,0 @@
<?php
/**
* Configuration
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Config Class
*
* Handles loading the site's configuration file (if available). At the moment
* this class is a very skewed Singleton. The plan is to eventually extend this
* out to support multiple configuration files, and the ability to load in
* custom config files on the fly as well. The core of Pickles uses the class
* as a Singleton so we're not loading the configuration multiple times per
* page load.
*/
class Config extends \ArrayObject
{
private static $_instance = false;
/**
* Constructor
*
* Calls the parent constructor and loads the passed file.
*/
public function __construct($config_filename = false)
{
try
{
ini_set('display_errors', true);
error_reporting(-1);
$filename = getcwd() . '/../../pickles.php';
$environments = false;
$environment = false;
// Why not PHP_SAPI? because I wanted it to be convenient to unit test
$cli = !isset($_SERVER['REQUEST_METHOD']);
if ($config_filename)
{
$filename = $config_filename;
}
// Only require in case you want to reload the config
require $filename;
// Checks that we have the config array
if (!isset($config))
{
throw new \Exception('Missing $config array.');
}
// Determines the environment
if (!isset($config['environments']) || !is_array($config['environments']))
{
throw new \Exception('Environments are misconfigured.');
}
$environments = $config['environments'];
// If we're on the CLI, check an environment was even passed in
if ($cli && $_SERVER['argc'] < 2)
{
throw new \Exception('You must pass an environment (e.g. php script.php <environment>)');
}
// Loops through the environments and looks for a match
foreach ($config['environments'] as $name => $hosts)
{
if (!is_array($hosts))
{
$hosts = [$hosts];
}
// Tries to determine the environment name
foreach ($hosts as $host)
{
if ($cli)
{
// Checks the first argument on the command line
if ($_SERVER['argv'][1] == $name)
{
$environment = $name;
break;
}
}
else
{
// Exact match
if ((preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $host)
&& $_SERVER['SERVER_ADDR'] == $host)
|| (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] == $host))
{
$environment = $name;
break;
}
// Fuzzy match
elseif (substr($host, 0, 1) == '/'
&& (preg_match($host, $_SERVER['SERVER_NAME'], $matches) > 0
|| preg_match($host, $_SERVER['HTTP_HOST'], $matches) > 0))
{
$environments[$name] = $matches[0];
$environment = $name;
$config['environments'][$name] = $matches[0];
break;
}
}
}
}
if (!$environment)
{
throw new \Exception('Unable to determine the environment.');
}
// Flattens the array based on the environment
$config = $this->flatten($environment, $config);
// Disables display errors in production
if ($environment == 'production')
{
ini_set('display_errors', false);
}
// Assigns the environment
$config['environment'] = $environment;
// Defaults expected Pickles variables to false
foreach (['auth', 'cache', 'profiler'] as $variable)
{
if (!isset($config[$variable]))
{
$config[$variable] = false;
}
}
// Assigns the config variables to the object
foreach ($config as $variable => $value)
{
$this[$variable] = $value;
}
}
catch (\Exception $e)
{
throw $e;
}
}
/**
* Flatten
*
* Flattens the configuration array around the specified environment.
*
* @param string $environment selected environment
* @param array $array configuration error to flatten
* @return array flattened configuration array
*/
private function flatten($environment, $array)
{
if (is_array($array))
{
foreach ($array as $key => $value)
{
if (is_array($value))
{
if (isset($value[$environment]))
{
$value = $value[$environment];
}
else
{
$value = $this->flatten($environment, $value);
}
}
$array[$key] = $value;
}
}
return $array;
}
/**
* Get instance of the object
*
* Let's the parent class do all the work
*
* @static
* @param string $file name of config to load
* @return object self::$_instance instance of the Config class
*/
public static function getInstance($file = false)
{
if (!self::$_instance || $file)
{
self::$_instance = new Config($file);
}
return self::$_instance;
}
}

View file

@ -1,50 +0,0 @@
<?php
/**
* Mongo Abstraction Layer
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
class Mongo extends Object
{
public static function getInstance($class = 'Mongo')
{
$config = Config::getInstance();
if (!isset(self::$instances['Mongo']))
{
if (!isset($config['mongo'], $config['mongo']['database']))
{
throw new \Exception('The “mongo” datasource is not defined in the configuration.', 500);
}
$mongo = $config['mongo'];
// Defaults to the local server on the default port
if (!isset($mongo['server']))
{
$mongo['server'] = 'mongodb://localhost:27017';
}
// Instantiates our Mongo client
$instance = new \MongoClient($mongo['server']);
$instance->selectDB($mongo['database']);
// Caches the instance for possible reuse later
self::$instances['Mongo'] = $instance;
}
// Returns the instance
return self::$instances['Mongo'];
}
}

View file

@ -1,84 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Entity\AbstractTokenEntity;
use \League\OAuth2\Server\Entity\AccessTokenEntity;
use \League\OAuth2\Server\Entity\ScopeEntity;
use \League\OAuth2\Server\Storage\AccessTokenInterface;
class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface
{
public function get($token)
{
$sql = 'SELECT oauth_access_tokens.*'
. ' FROM oauth_access_tokens'
. ' WHERE access_token = ?'
. ' AND expires_at >= ?;';
$results = $this->db->fetch($sql, [$token, time()]);
if (count($results) === 1)
{
return (new AccessTokenEntity($this->server))
->setId($results[0]['access_token'])
->setExpireTime($results[0]['expires_at']);
}
return null;
}
public function getScopes(AbstractTokenEntity $token)
{
$sql = 'SELECT oauth_scopes.id, oauth_scopes.description'
. ' FROM oauth_access_token_scopes'
. ' INNER JOIN oauth_scopes'
. ' ON oauth_access_token_scopes.scope_id = oauth_scopes.id'
. ' WHERE oauth_access_token_scopes.access_token_id = ?;';
$results = $this->db->fetch($sql, [$token->getId()]);
$response = [];
if (count($results) > 0)
{
foreach ($results as $row)
{
$response[] = (new ScopeEntity($this->server))->hydrate([
'id' => $row['id'],
'description' => $row['description']
]);
}
}
return $response;
}
public function create($token, $expiration, $session_id)
{
$sql = 'INSERT INTO oauth_access_tokens'
. ' (access_token, session_id, expires_at)'
. ' VALUES'
. ' (?, ?, ?);';
$this->db->execute($sql, [$token, $session_id, $expiration]);
}
public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope)
{
$sql = 'INSERT INTO oauth_access_token_scopes'
. ' (access_token, scope)'
. ' VALUES'
. ' (?, ?);';
$this->db->execute($sql, [$token->getId(), $scope->getId()]);
}
public function delete(AbstractTokenEntity $token)
{
$sql = 'DELETE FROM oauth_access_token_scopes'
. ' WHERE access_token = ?;';
$this->db->execute($sql, [$token->getId()]);
}
}

View file

@ -1,81 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Entity\ClientEntity;
use \League\OAuth2\Server\Entity\SessionEntity;
use \League\OAuth2\Server\Storage\Adapter;
use \League\OAuth2\Server\Storage\ClientInterface;
class ClientStorage extends StorageAdapter implements ClientInterface
{
public function get($client_id, $client_secret = null, $redirect_uri = null, $grant_type = null)
{
$sql = 'SELECT oauth_clients.*';
if ($redirect_uri)
{
$sql .= ', oauth_client_redirect_uris.*'
. ' INNER JOIN oauth_redirect_uris'
. ' ON oauth_clients.id = oauth_redirect_uris.client_id';
}
$sql .= ' FROM oauth_clients WHERE oauth_clients.id = ?';
$parameters = [$client_id];
if ($client_secret)
{
$sql .= ' AND oauth_clients.secret = ?';
$parameters[] = $client_secret;
}
if ($redirect_uri)
{
$sql .= 'AND oauth_redirect_uris.redirect_uri = ?';
$parameters[] = $redirect_uri;
}
$results = $this->db->fetch($sql, $parameters);
if (count($results) === 1)
{
$client = new ClientEntity($this->server);
$client->hydrate([
'id' => $results[0]['id'],
'name' => $results[0]['name']
]);
return $client;
}
return null;
}
public function getBySession(SessionEntity $session)
{
$sql = 'SELECT oauth_clients.id, oauth_clients.name'
. ' FROM oauth_clients'
. ' INNER JOIN oauth_sessions'
. ' ON oauth_clients.id = oauth_sessions.client_id'
. ' WHERE oauth_sessions.id = ?';
$results = $this->db->fetch($sql, [$session->getId()]);
if (count($results) === 1)
{
$client = new ClientEntity($this->server);
$client->hydrate([
'id' => $results[0]['id'],
'name' => $results[0]['name']
]);
return $client;
}
return null;
}
}

View file

@ -1,56 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Entity\RefreshTokenEntity;
use \League\OAuth2\Server\Storage\RefreshTokenInterface;
class RefreshTokenStorage extends StorageAdapter implements RefreshTokenInterface
{
public function get($token)
{
$sql = 'SELECT oauth_refresh_tokens.*'
. ' FROM oauth_refresh_tokens'
. ' WHERE refresh_token = ?'
. ' AND expires_at >= ?;';
$results = $this->db->fetch($sql, [$token, time()]);
if (count($results) === 1)
{
return (new RefreshTokenEntity($this->server))
->setId($results[0]['refresh_token'])
->setExpireTime($results[0]['expires_at'])
->setAccessTokenId($results[0]['access_token_id']);
}
return null;
}
public function create($token, $expiration, $access_token)
{
$sql = 'SELECT id FROM oauth_access_tokens WHERE access_token = ?;';
$results = $this->db->fetch($sql, [$access_token]);
$token_id = $results[0]['id'];
$sql = 'INSERT INTO oauth_refresh_tokens'
. ' (refresh_token, access_token_id, expires_at, client_id)'
. ' VALUES'
. ' (?, ?, ?, ?);';
$this->db->execute($sql, [
$token,
$token_id,
$expiration,
$this->server->getRequest()->request->get('client_id', null),
]);
}
public function delete(RefreshTokenEntity $token)
{
$sql = 'DELETE FROM oauth_refresh_tokens WHERE refresh_token = ?;';
$this->db->execute($sql, [$token->getId()]);
}
}

View file

@ -1,141 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Exception\OAuthException;
use \League\OAuth2\Server\AuthorizationServer;
use \League\OAuth2\Server\Grant\PasswordGrant;
use \League\OAuth2\Server\Grant\RefreshTokenGrant;
use \Pickles\App\Models\User;
use \Pickles\Config;
class Resource extends \Pickles\Resource
{
public function POST()
{
if (!isset($this->config['oauth'][$_SERVER['__version']]))
{
throw new \Exception('Forbidden.', 403);
}
elseif (!isset($_REQUEST['grant_type']))
{
throw new \Exception('Bad Request.', 400);
}
$config = $this->config['oauth'][$_SERVER['__version']];
switch (substr($_REQUEST['request'], strlen($_SERVER['__version']) + 2))
{
case 'oauth/access_token':
try
{
$server = new AuthorizationServer;
$server->setSessionStorage(new SessionStorage);
$server->setAccessTokenStorage(new AccessTokenStorage);
$server->setClientStorage(new ClientStorage);
$server->setScopeStorage(new ScopeStorage);
$server->setRefreshTokenStorage(new RefreshTokenStorage);
$grant_type = $_REQUEST['grant_type'];
$grants = ['password'];
if (isset($config['grants']))
{
$grants = array_unique(array_merge($grants, $config['grants']));
}
if (!in_array($grant_type, $grants))
{
throw new \Exception('Unsupported grant type.', 403);
}
// Defaults TTLs to 1 day and 1 week respectively
$token_ttl = 3600;
$refresh_ttl = 604800;
if (isset($config['ttl']['access_token']))
{
$token_ttl = $config['ttl']['access_token'];
}
switch ($grant_type)
{
case 'authorization_code':
throw new \Exception('Not Implemented', 501);
break;
case 'client_credentials':
throw new \Exception('Not Implemented', 501);
break;
case 'implicit':
throw new \Exception('Not Implemented', 501);
break;
case 'password':
$grant = new PasswordGrant;
$grant->setAccessTokenTTL($token_ttl);
$grant->setVerifyCredentialsCallback(function ($username, $password)
{
$user = new User([
'conditions' => [
'email' => $username,
],
]);
return $user->count()
&& password_verify($password, $user->record['password']);
});
break;
case 'refresh_token':
throw new \Exception('Not Implemented', 501);
// @todo Need to work through this, appears lib is busted
$grant = new RefreshTokenGrant;
//$grant->setAccessTokenTTL($refresh_ttl);
$server->addGrantType($grant);
break;
}
$server->addGrantType($grant);
// Adds the refresh token grant if enabled
if ($grant_type != 'refresh_token'
&& in_array('refresh_token', $grants))
{
if (isset($config['ttl']['refresh_token']))
{
$refresh_ttl = $config['ttl']['refresh_token'];
}
$grant = new RefreshTokenGrant;
$grant->setAccessTokenTTL($refresh_ttl);
$server->addGrantType($grant);
}
$response = $server->issueAccessToken();
return $response;
}
catch (OAuthException $e)
{
throw new \Exception($e->getMessage(), $e->httpStatusCode);
}
catch (\Exception $e)
{
throw new \Exception($e->getMessage(), $e->getCode());
}
break;
default:
throw new \Exception('Not Found.', 404);
break;
}
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Storage\Adapter;
use \League\OAuth2\Server\Storage\ScopeInterface;
class ScopeStorage extends StorageAdapter implements ScopeInterface
{
public function get($scope, $grant_type = null, $client_id = null)
{
$sql = 'SELECT * FROM oauth_scopes WHERE id = ?;';
$results = $this->db->fetch($sql, [$scope]);
if (count($results) === 0)
{
return null;
}
return (new ScopeEntity($this->server))->hydrate([
'id' => $result[0]['id'],
'description' => $result[0]['description'],
]);
}
}

View file

@ -1,106 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Entity\AccessTokenEntity;
use \League\OAuth2\Server\Entity\AuthCodeEntity;
use \League\OAuth2\Server\Entity\ScopeEntity;
use \League\OAuth2\Server\Entity\SessionEntity;
use \League\OAuth2\Server\Storage\Adapter;
use \League\OAuth2\Server\Storage\SessionInterface;
class SessionStorage extends StorageAdapter implements SessionInterface
{
public function getByAccessToken(AccessTokenEntity $access_token)
{
$sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,'
. ' oauth_sessions.owner_id, oauth_sessions.client_id,'
. ' oauth_sessions.client_redirect_uri'
. ' FROM oauth_sessions'
. ' INNER JOIN oauth_access_tokens'
. ' ON oauth_access_tokens.session_id = oauth_sessions.id'
. ' WHERE oauth_access_tokens.access_token = ?;';
$results = $this->db->fetch($sql, [$access_token->getId()]);
if (count($results) === 1)
{
$session = new SessionEntity($this->server);
$session->setId($result[0]['id']);
$session->setOwner($result[0]['owner_type'], $result[0]['owner_id']);
return $session;
}
return null;
}
public function getByAuthCode(AuthCodeEntity $auth_code)
{
$sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,'
. ' oauth_sessions.owner_id, oauth_sessions.client_id,'
. ' oauth_sessions.client_redirect_uri'
. ' FROM oauth_sessions'
. ' INNER JOIN oauth_authorization_codes'
. ' ON oauth_authorization_codes.session_id = oauth_sessions.id'
. ' WHERE oauth_authorization_codes.authorization_code = ?;';
$results = $this->db->fetch($sql, [$auth_code->getId()]);
if (count($results) === 1)
{
$session = new SessionEntity($this->server);
$session->setId($result[0]['id']);
$session->setOwner($result[0]['owner_type'], $result[0]['owner_id']);
return $session;
}
return null;
}
public function getScopes(SessionEntity $session)
{
$sql = 'SELECT oauth_sessions.*'
. ' FROM oauth_sessions'
. ' INNER JOIN oauth_access_token_scopes'
. ' ON oauth_sessions.id = oauth_access_token_scopes.access_token_id'
. ' INNER JOIN oauth_scopes'
. ' ON oauth_scopes.id = oauth_access_token_scopes.scope_id'
. ' WHERE oauth_sessions.id = ?;';
$results = $this->db->fetch($sql, [$session->getId()]);
$scopes = [];
foreach ($results as $scope)
{
$scopes[] = (new ScopeEntity($this->server))->hydrate([
'id' => $scope['id'],
'description' => $scope['description'],
]);
}
return $scopes;
}
public function create($owner_type, $owner_id, $client_id, $client_redirect_uri = null)
{
$sql = 'INSERT INTO oauth_sessions'
. ' (owner_type, owner_id, client_id)'
. ' VALUES'
. ' (?, ?, ?);';
return $this->db->execute($sql, [$owner_type, $owner_id, $client_id]);
}
public function associateScope(SessionEntity $session, ScopeEntity $scope)
{
$sql = 'INSERT INTO oauth_access_token_scopes'
. ' (access_token_id, scope_id)'
. ' VALUES'
. ' (?, ?);';
$this->db->execute($sql, [$session->getId(), $scope->getId()]);
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace Pickles\OAuth2;
use \League\OAuth2\Server\Storage\Adapter;
use \Pickles\Config;
use \Pickles\Database;
class StorageAdapter extends Adapter
{
protected $config;
protected $db;
public function __construct()
{
$this->config = Config::getInstance();
$this->db = Database::getInstance();
}
}

View file

@ -1,114 +0,0 @@
<?php
/**
* Parent Object
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Object Class
*
* Every instantiated class in Pickles should be extending this class. By doing
* so the class is automatically hooked into the profiler, and the object will
* have access to some common components as well.
*/
class Object
{
/**
* Object Instances
*
* @static
* @var array
*/
public static $instances = [];
/**
* Instance of the Config object
*
* @var object
*/
public $config = null;
/**
* Instance of the Mongo object
*
* @var object
*/
public $mongo = null;
/**
* Instance of the Redis object
*
* @var object
*/
public $redis = null;
/**
* Constructor
*
* Establishes a Config instance for all children to enjoy
*/
public function __construct()
{
// @todo Lazy load these so we're not loading them on every instance
$this->config = Config::getInstance();
$this->mongo = Mongo::getInstance();
//$this->redis = Redis::getInstance();
// Optionally logs the constructor to the profiler
if ($this->config['profiler'])
{
Profiler::log($this, '__construct');
}
}
/**
* Get Instance
*
* Gets an instance of the passed class. Allows for easy sharing of certain
* classes within the system to avoid the extra overhead of creating new
* objects each time. Also avoids the hassle of passing around variables.
*
* @static
* @param string $class name of the class
* @return object instance of the class
*/
public static function getInstance($class = false)
{
if ($class)
{
$class = 'Pickles\\' . $class;
if (!isset(self::$instances[$class]))
{
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
return false;
}
/**
* Destructor
*/
public function __destruct()
{
// Optionally logs the destructor to the profiler
if ($this->config['profiler'])
{
Profiler::log($this, '__destruct');
}
}
}

View file

@ -1,196 +0,0 @@
<?php
/**
* Profiler
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Profiler Class
*
* The Profiler class is statically interfaced with and allows for in depth
* profiling of a site. By default profiling is off, but can be enabled in the
* config.ini for a site. Out of the box the profiler will report on every
* class object in the system that extends the code Object class.
*
* @usage <code>Profiler::log('some action you want to track');</code>
* @usage <code>Profiler::log($object, 'methodName');</code>
*/
class Profiler
{
/**
* Logs
*
* Array of logged events
*
* @static
* @access private
* @var array
*/
private static $logs = [];
/**
* Timers
*
* Array of active timers
*
* @static
* @access private
* @var array
*/
private static $timers = [];
/**
* Log
*
* Logs the event to be displayed later on. Due to the nature of how much
* of a pain it is to determine which class method called this method I
* opted to make the method a passable argument for ease of use. Perhaps
* I'll revisit in the future. Handles all elapsed time calculations and
* memory usage.
*
* @static
* @param mixed $data data to log
* @param string $method name of the class method being logged
*/
public static function log($data, $method = false, $type = false)
{
$time = microtime(true);
$data_type = ($data == 'timer' ? $data : gettype($data));
// Tidys the data by type
switch ($data_type)
{
case 'object':
$details['class'] = get_class($data);
if ($method != '')
{
$details['method'] = $method . '()';
}
$data_type = $data_type;
break;
case 'timer':
$details = $method;
$data_type = $data_type;
break;
default:
if ($type != false)
{
$data_type = $type;
}
$details = $data;
break;
}
self::$logs[] = [
'type' => $data_type,
'timestamp' => $time,
'elapsed_time' => $time - $_SERVER['REQUEST_TIME_FLOAT'],
'memory_usage' => memory_get_usage(),
'details' => $details,
];
}
/**
* Query
*
* Serves as a wrapper to get query data to the log function
*
* @static
* @param string $query the query being executed
* @param array $input_parameters optional prepared statement data
* @param array $results optional results of the query
* @param float $duration the speed of the query
* @param array $explain EXPLAIN data for the query
*/
public static function query($query, $input_parameters = false, $results = false, $duration = false, $explain = false)
{
$log = [
'query' => $query,
'parameters' => $input_parameters,
'results' => $results,
'execution_time' => $duration,
];
if ($explain)
{
$log['explain'] = $explain;
}
self::log($log, false, 'database');
}
/**
* Timer
*
* Logs the start and end of a timer.
*
* @param string $timer name of the timer
* @return boolean whether or not timer profiling is enabled
*/
public static function timer($timer)
{
// Starts the timer
if (!isset(self::$timers[$timer]))
{
self::$timers[$timer] = microtime(true);
self::Log('timer', [
'action' => 'start',
'name' => $timer
]);
}
// Ends the timer
else
{
self::Log('timer', [
'action' => 'stop',
'name' => $timer,
'elapsed_time' => (microtime(true) - self::$timers[$timer])
]);
unset(self::$timers[$timer]);
}
}
/**
* Report
*
* Generates the Profiler report that is displayed by the Controller.
* Contains all the HTML needed to display the data properly inline on the
* page. Will generally be displayed after the closing HTML tag.
*/
public static function report()
{
$report = [
'request_time' => $_SERVER['REQUEST_TIME_FLOAT'],
'execution_time' => self::$logs[count(self::$logs) - 1]['timestamp']
- $_SERVER['REQUEST_TIME_FLOAT'],
'peak_memory_usage' => memory_get_peak_usage(),
'max_execution_time' => ini_get('max_execution_time'),
'memory_limit' => ini_get('memory_limit'),
'included_files' => count(get_included_files()),
'logs' => self::$logs,
];
self::$logs = [];
self::$timers = [];
return $report;
}
}

View file

@ -1,387 +0,0 @@
<?php
/**
* Resource Class
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
use \League\OAuth2\Server\ResourceServer;
use Pickles\OAuth2\AccessTokenStorage;
use Pickles\OAuth2\ClientStorage;
use Pickles\OAuth2\ScopeStorage;
use Pickles\OAuth2\SessionStorage;
/**
* Resource Class
*
* This is a parent class that all Pickles modules should be extending. Each
* module can specify it's own meta data and whether or not a user must be
* properly authenticated to view the page. Currently any pages without a
* template are treated as pages being requested via AJAX and the return will
* be JSON encoded. In the future this may need to be changed out for logic
* that allows the requested module to specify what display type(s) it can use.
*/
class Resource extends Object
{
/**
* Filter
*
* Variables to filter.
*
* @var array
*/
public $filter = [];
/**
* Validate
*
* Variables to validate.
*
* @var array
*/
public $validate = [];
// @todo Document this
public $description = [];
public $auth = false;
public $status = 200;
public $message = 'OK';
public $echo = false;
public $limit = false;
public $offset = false;
public $errors = [];
public $uids = [];
public $response = false;
public $profiler = false;
/**
* Constructor
*
* The constructor does nothing by default but can be passed a boolean
* variable to tell it to automatically run the __default() method. This is
* typically used when a module is called outside of the scope of the
* controller (the registration page calls the login page in this manner.
*/
public function __construct($uids = false)
{
parent::__construct();
$this->uids = $uids;
$method = $_SERVER['REQUEST_METHOD'];
try
{
// Checks if auth flag is explicity true or true for the method
if ($this->auth === true
|| (isset($this->auth[$method]) && $this->auth[$method]))
{
if (isset($this->config['oauth'][$_SERVER['__version']]))
{
$server = new ResourceServer(
new SessionStorage,
new AccessTokenStorage,
new ClientStorage,
new ScopeStorage
);
$server->isValidRequest();
}
else
{
throw new \Exception('Authentication is not configured properly.', 401);
}
}
// Hacks together some new globals
if (in_array($method, ['PUT', 'DELETE']))
{
$GLOBALS['_' . $method] = [];
// @todo Populate it
}
$filter = isset($this->filter[$method]);
$validate = isset($this->validate[$method]);
if ($filter || $validate)
{
$global =& $GLOBALS['_' . $method];
// Checks that the required parameters are present
// @todo Add in support for uid:* variables
if ($validate)
{
$variables = [];
foreach ($this->validate[$method] as $variable => $rules)
{
if (!is_array($rules))
{
$variable = $rules;
}
$variables[] = $variable;
}
$missing_variables = array_diff($variables, array_keys($global));
if ($missing_variables !== array())
{
foreach ($missing_variables as $variable)
{
$this->errors[$variable][] = 'The ' . $variable . ' parameter is required.';
}
}
}
foreach ($global as $variable => $value)
{
// Applies any filters
if ($filter && isset($this->filter[$method][$variable]))
{
$function = $this->filter[$method][$variable];
if ($function == 'password_hash')
{
$global[$variable] = password_hash($value, PASSWORD_DEFAULT);
}
else
{
$global[$variable] = $function($value);
}
}
if ($validate && isset($this->validate[$method][$variable]))
{
$rules = $this->validate[$method][$variable];
if (is_array($rules))
{
if (isset($global[$variable]) && !String::isEmpty($global[$variable]))
{
if (is_array($rules))
{
foreach ($rules as $rule => $message)
{
$rule = explode(':', $rule);
for ($i = 1; $i <= 2; $i++)
{
if (!isset($rule[$i]))
{
$rule[$i] = false;
}
}
switch ($rule[0])
{
// {{{ Checks using filter_var()
case 'filter':
switch ($rule[1])
{
case 'boolean':
case 'email':
case 'float':
case 'int':
case 'ip':
case 'url':
$filter = constant('FILTER_VALIDATE_' . strtoupper($rule[1]));
if (!filter_var($value, $filter))
{
$this->errors[$variable][] = $message;
}
break;
default:
$this->errors[$variable] = 'Invalid filter, expecting boolean, email, float, int, ip or url.';
break;
}
break;
// }}}
// {{{ Checks using strlen()
case 'length':
$length = strlen($value);
switch ($rule[1])
{
case '<':
$valid = $length < $rule[2];
break;
case '<=':
$valid = $length <= $rule[2];
break;
case '==':
$valid = $length == $rule[2];
break;
case '!=':
$valid = $length != $rule[2];
break;
case '>=':
$valid = $length >= $rule[2];
break;
case '>':
$valid = $length > $rule[2];
break;
default:
$valid = false;
$message = 'Invalid operator, expecting <, <=, ==, !=, >= or >.';
break;
}
if (!$valid)
{
$this->errors[$variable][] = $message;
}
break;
// }}}
// {{{ Checks using preg_match()
case 'regex':
if (preg_match($rule[1], $value))
{
$this->errors[$variable][] = $message;
}
break;
// }}}
// @todo case 'alpha':
// @todo case 'alphanumeric':
// @todo case 'date':
// @todo case 'range':
}
}
}
}
}
}
}
// if PUT or DELETE, need to update the super globals directly as
// they do not stay in sync. Probably need to make them global in
// this class method
//
// $_PUT = $GLOBALS['_PUT'];
}
if ($this->errors)
{
throw new \Exception('Missing or invalid parameters.', 400);
}
parent::__construct();
// Checks if the request method has been implemented
if (get_class($this) != 'Pickles\\Resource')
{
if (!method_exists($this, $method))
{
throw new \Exception('Method not allowed.', 405);
}
else
{
// Starts a timer before the resource is executed
if ($this->config['profiler'])
{
$timer = get_class($this) . '->' . $method . '()';
Profiler::timer($timer);
}
$this->response = $this->$method();
// Stops the resource timer
if ($this->config['profiler'])
{
Profiler::timer($timer);
}
}
}
}
catch (\Exception $e)
{
$code = $e->getCode();
// Anything below 200 is probably a PHP error
if ($code < 200)
{
$code = 500;
}
$this->status = $code;
$this->message = $e->getMessage();
}
}
public function respond()
{
http_response_code($this->status);
header('Content-Type: application/json');
header('X-Powered-By: Pickles (http://picklesphp.com)');
$meta = [
'status' => $this->status,
'message' => $this->message,
];
// Forces errors to be an array of arrays
if ($this->errors)
{
foreach ($this->errors as $key => $error)
{
if (!is_array($error))
{
$this->errors[$key] = [$error];
}
}
}
foreach (['echo', 'limit', 'offset', 'errors'] as $variable)
{
if ($this->$variable)
{
$meta[$variable] = $this->$variable;
}
}
$response = ['meta' => $meta];
foreach (['response', 'profiler'] as $variable)
{
if ($this->$variable)
{
$response[$variable] = $this->$variable;
}
}
if ($this->config['profiler'])
{
$response['profiler'] = Profiler::report();
}
$pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false;
echo json_encode($response, $pretty);
}
}

View file

@ -1,113 +0,0 @@
<?php
/**
* Endpoint Router
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Router Class
*
* The heavy lifter of Pickles, makes the calls to get the session and
* configuration loaded. Loads modules, serves up user authentication when the
* module asks for it, and loads the viewer that the module requested. Default
* values are present to make things easier on the user.
*
* @usage <code>new Pickles\Router;</code>
*/
class Router extends Object
{
/**
* Constructor
*
* To save a few keystrokes, the Controller is executed as part of the
* constructor instead of via a method. You either want the Controller or
* you don't.
*/
public function __construct()
{
parent::__construct();
try
{
// Secure by default
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)
{
throw new \Exception('HTTPS is required.', 400);
}
// Grabs the requested page
$request = $_REQUEST['request'];
$components = explode('/', $request);
$nouns = [];
$uids = [];
$version = array_shift($components);
$_SERVER['__version'] = substr($version, 1);
// Checks if we're trying to rock some OAuth
if ($components[0] == 'oauth')
{
$class = 'Pickles\OAuth2\Resource';
}
else
{
// Loops through the components to determine nouns and IDs
foreach ($components as $index => $component)
{
if ($index % 2)
{
$uids[end($nouns)] = $component;
}
else
{
$nouns[] = $component;
}
}
// Creates our class name
array_unshift($nouns, '', 'Pickles', 'App', 'Resources', $version);
$class = implode('\\', $nouns);
}
// Checks that the file is present and contains our class
if (!class_exists($class))
{
throw new \Exception('Not Found.', 404);
}
// Instantiates our resource with the UIDs
$resource = new $class($uids);
}
catch (\Exception $e)
{
// Creates a resource object if we don't have one
if (!isset($resource))
{
$resource = new Resource;
}
$code = $e->getCode();
// Anything below 200 is probably a PHP error
if ($code < 200)
{
$code = 500;
}
$resource->status = $code;
$resource->message = $e->getMessage();
}
$resource->respond();
}
}

View file

@ -1,245 +0,0 @@
<?php
/**
* String Utility Collection
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* String Class
*
* Just a simple collection of static functions to accomplish some of the
* more redundant string related manipulation.
*/
class String
{
// {{{ Format Phone Number
/**
* Format Phone Number
*
* Formats a 10 digit phone number with dashes as ###-###-####.
*
* @static
* @param string $number number to format
* @param string $replacement output of the string
* @return string formatted phone number
*/
public static function formatPhoneNumber($number, $replacement = '$1-$2-$3')
{
// Strips characters we don't need
$number = str_replace(['(', ')', ' ', '-', '.', '_'], '', $number);
// Formats the number
return preg_replace('/^(\d{3})(\d{3})(.+)$/', $replacement, $number);
}
// }}}
// {{{ Generate Slug
/**
* Generate Slug
*
* Generates a slug from the pass string by lowercasing the string,
* trimming whitespace and converting non-alphanumeric values to
* dashes. Takes care of multiple dashes as well.
*
* @static
* @param string $string to be converted to the slug
* @return string resulting slug
*/
public static function generateSlug($string)
{
$string = strtolower(trim($string));
$string = preg_replace('/[^a-z0-9-]/', '-', $string);
$string = preg_replace('/-+/', '-', $string);
return trim($string, '-');
}
// }}}
// {{{ Is Empty
/**
* Is Empty
*
* Checks if a string is empty. You can use the PHP function empty()
* but that returns true for a string of "0". Last I checked, that's
* not an empty string. PHP's function also doesn't apply trim() to the
* value to ensure it's not just a bunch of spaces.
*
* @static
* @param string $value string(s) to be checked
* @return boolean whether or not the string is empty
*/
public static function isEmpty()
{
foreach (func_get_args() as $value)
{
if (trim($value) == '')
{
return true;
}
}
return false;
}
// }}}
// {{{ Pluralize
/**
* Pluralize
*
* Based on a passed integer, the word will be pluralized. A value of
* zero will also pluralize the word (e.g. 0 things not 0 thing).
*
* @static
* @param string $string the word to plurailze
* @param integer $count the count to interrogate
* @param boolean $both (optional) include count in return
* @return string pluralized word
*/
public static function pluralize($string, $count, $both = false)
{
if ($count != 1)
{
$string .= 's';
}
if ($both)
{
$string = $count . ' ' . $string;
}
return $string;
}
// }}}
// {{{ Random
/**
* Random
*
* Generates a pseudo-random string based on the passed parameters.
*
* Note: Similar characters = 0, O, 1, I (and may be expanded)
*
* @static
* @param integer $length optional length of the generated string
* @param boolean $alpha optional include alpha characters
* @param boolean $numeric optional include numeric characters
* @param boolean $similar optional include similar characters
* @return string generated string
*/
public static function random($length = 8, $alpha = true, $numeric = true, $similar = true)
{
$characters = [];
$string = '';
// Adds alpha characters to the list
if ($alpha == true)
{
if ($similar == true)
{
$characters = array_merge($characters, range('a', 'z'));
}
else
{
$characters = array_merge($characters, range('a', 'h'), range('j', 'n'), range('p', 'z'));
}
}
// Adds numeric characters to the list
if ($numeric == true)
{
if ($similar == true)
{
$characters = array_merge($characters, range('0', '9'));
}
else
{
$characters = array_merge($characters, range('2', '9'));
}
}
if (count($characters) > 0)
{
shuffle($characters);
for ($i = 0; $i < $length; $i++)
{
$string .= $characters[array_rand($characters)];
}
}
return $string;
}
// }}}
// {{{ Truncate
/**
* Truncate
*
* Truncates a string to a specified length and (optionally) adds a
* span to provide a rollover to see the expanded text.
*
* @static
* @param string $string string to truncate
* @param integer $length length to truncate to
* @param boolean $hover (optional) whether or not to add the rollover
* @return string truncate string
*/
public static function truncate($string, $length, $hover = true)
{
if (strlen($string) > $length)
{
if ($hover == true)
{
$string = '<span title="' . $string . '">' . mb_strcut($string, 0, $length, 'UTF-8') . '&hellip;</span>';
}
else
{
$string = mb_strcut($string, 0, $length, 'UTF-8') . '&hellip;';
}
}
return $string;
}
// }}}
// {{{ Upper Words
/**
* Upper Words
*
* Applies strtolower() and ucwords() to the passed string. The
* exception being email addresses which are not formatted at all.
*
* @static
* @param string $string string to format
* @return string formatted string
*/
public static function upperWords($string)
{
// Only formats non-email addresses
if (filter_var($string, FILTER_VALIDATE_EMAIL) == false)
{
$string = ucwords(strtolower($string));
}
return $string;
}
// }}}
}

View file

@ -1,271 +0,0 @@
<?php
/**
* Time Utility Collection
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Time Class
*
* Just a simple collection of static functions to accomplish some of the more
* redundant time and date related manipulation.
*/
class Time
{
// {{{ Intervals (in seconds)
/**
* Minute
*
* Seconds in a minute
*
* @var integer
*/
const MINUTE = 60;
/**
* Hour
*
* Seconds in an hour (minute * 60)
*
* @var integer
*/
const HOUR = 3600;
/**
* Day
*
* Seconds in a day (hour * 24)
*
* @var integer
*/
const DAY = 86400;
/**
* Week
*
* Seconds in a week (day * 7)
*
* @var integer
*/
const WEEK = 604800;
/**
* Month
*
* Seconds in a month (day * 30)
*
* @var integer
*/
const MONTH = 2592000;
/**
* Quarter
*
* Seconds in a quarter (day * 90)
*
* @var integer
*/
const QUARTER = 7776000;
/**
* Year
*
* Seconds in a year (day * 365)
*
* @var integer
*/
const YEAR = 31536000;
/**
* Decade
*
* Seconds in a decade (year * 10)
*
* @var integer
*/
const DECADE = 315360000;
/**
* Century
*
* Seconds in a decade (decade * 10)
*
* @var integer
*/
const CENTURY = 3153600000;
// }}}
/**
* Age
*
* Calculates age based on the passed date.
*
* @static
* @param string $date birth / inception date
* @return integer $age number of years old
*/
public static function age($date)
{
if (!preg_match('/\d{4}-\d{2}-\d{2}/', $date))
{
$date = date('Y-m-d', strtotime($date));
}
list($year, $month, $day) = explode('-', $date, 3);
$age = date('Y') - $year;
if (date('md') < $month . $day)
{
$age--;
}
return $age;
}
/**
* Ago
*
* Generates a relative time (e.g. X minutes ago).
*
* @static
* @param mixed $time timestamp to calculate from
* @return string relative time
*/
public static function ago($time)
{
$current = strtotime(Time::timestamp());
$time = preg_match('/^\d+$/', $time) ? $time : strtotime($time);
if ($current == $time)
{
$time_ago = 'just now';
}
else
{
if ($current > $time)
{
$difference = $current - $time;
$suffix = ' ago';
}
else
{
$difference = $time - $current;
$suffix = ' from now';
}
// Less than 1 minute ago (seconds ago)
if ($difference < 60)
{
$time_ago = 'seconds';
}
// Less than 1 hour ago (minutes ago)
elseif ($difference < Time::HOUR)
{
$minutes = round($difference / 60);
if ($minutes == 60)
{
$time_ago = 'an hour';
}
else
{
$time_ago = ($minutes == 1 ? 'a' : $minutes) . ' minute' . ($minutes != 1 ? 's' : '');
}
}
// Less than 1 day ago (hours ago)
elseif ($difference < Time::DAY)
{
$hours = round($difference / Time::HOUR);
if ($hours == 24)
{
$time_ago = 'a day';
}
else
{
$time_ago = ($hours == 1 ? 'an' : $hours) . ' hour' . ($hours != 1 ? 's' : '');
}
}
// Less than 1 week ago (days ago)
elseif ($difference < Time::WEEK)
{
$days = round($difference / Time::DAY);
if ($days == 7)
{
$time_ago = 'a week';
}
else
{
$time_ago = ($days == 1 ? 'a' : $days) . ' day' . ($days != 1 ? 's' : '');
}
}
// Less than 1 month ago (weeks ago)
elseif ($difference < Time::MONTH)
{
$weeks = round($difference / Time::WEEK);
if ($weeks == 4)
{
$time_ago = 'a month';
}
else
{
$time_ago = ($weeks == 1 ? 'a' : $weeks) . ' week' . ($weeks != 1 ? 's' : '');
}
}
// Less than 1 year ago (months ago)
elseif ($difference < Time::YEAR)
{
$months = round($difference / Time::MONTH);
if ($months == 12)
{
$time_ago = 'a year';
}
else
{
$time_ago = ($months == 1 ? 'a' : $months) . ' month' . ($months != 1 ? 's' : '');
}
}
// Over 1 year ago (years ago)
else
{
$years = round($difference / Time::YEAR);
$time_ago = ($years == 1 ? 'a' : $years) . ' year' . ($years != 1 ? 's' : '');
}
$time_ago .= $suffix;
}
return $time_ago;
}
/**
* Timestamp
*
* Current Universal Time in the specified format.
*
* @static
* @param string $format format of the timestamp
* @return string $timestamp formatted timestamp
*/
public static function timestamp($format = 'Y-m-d H:i:s')
{
return gmdate($format);
}
}

View file

@ -1,174 +0,0 @@
<?php
class ConfigTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
touch('/tmp/pickles.php');
}
public static function tearDownAfterClass()
{
unlink('/tmp/pickles.php');
}
/**
* @expectedException Exception
* @expectedExceptionMessage Missing $config array.
*/
public function testMissingConfig()
{
file_put_contents('/tmp/pickles.php', '');
new Pickles\Config('/tmp/pickles.php');
}
/**
* @expectedException Exception
* @expectedExceptionMessage Environments are misconfigured.
*/
public function testMissingEnvironments()
{
file_put_contents('/tmp/pickles.php', '<?php
$config = [];
');
new Pickles\Config('/tmp/pickles.php');
}
/**
* @expectedException Exception
* @expectedExceptionMessage You must pass an environment (e.g. php script.php <environment>)
*/
public function testMissingCLIEnvironment()
{
$_SERVER['argc'] = 1;
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
];
');
new Pickles\Config('/tmp/pickles.php');
}
/**
* @expectedException Exception
* @expectedExceptionMessage You must pass an environment (e.g. php script.php <environment>)
*/
public function testCLIEnvironmentMissingParameter()
{
$_SERVER['argc'] = 1;
new Pickles\Config('/tmp/pickles.php');
}
public function testEnvironmentMatchCLI()
{
$_SERVER['argc'] = 2;
$_SERVER['argv'][1] = 'local';
$config = new Pickles\Config('/tmp/pickles.php');
$this->assertEquals('local', $config['environment']);
}
public function testEnvironmentMatchExact()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$config = new Pickles\Config('/tmp/pickles.php');
$this->assertEquals('local', $config['environment']);
}
public function testEnvironmentMatchFuzzy()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "/127\.0\.0\.[0-9]+/",
"production" => "123.456.789.0",
],
];
');
$config = new Pickles\Config('/tmp/pickles.php');
$this->assertEquals('local', $config['environment']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage Unable to determine the environment.
*/
public function testEnvironmentNoMatch()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = 'lolnope';
new Pickles\Config('/tmp/pickles.php');
}
public function testProductionDisplayErrors()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['HTTP_HOST'] = '123.456.789.0';
ini_set('display_errors', true);
$this->assertEquals('1', ini_get('display_errors'));
new Pickles\Config('/tmp/pickles.php');
$this->assertEquals('', ini_get('display_errors'));
}
public function testFlatten()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['HTTP_HOST'] = '123.456.789.0';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "/127\.0\.0\.[0-9]+/",
"production" => "123.456.789.0",
],
"foo" => [
"local" => "barLocal",
"production" => "barProduction",
],
"nestedOne" => [
"nestedTwo" => [
"local" => "nestedLocal",
"production" => "nestedProduction",
],
],
];
');
$config = new Pickles\Config('/tmp/pickles.php');
$this->assertEquals('barProduction', $config['foo']);
$this->assertEquals('nestedProduction', $config['nestedOne']['nestedTwo']);
}
public function testGetInstance()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['HTTP_HOST'] = '123.456.789.0';
$config = Pickles\Config::getInstance('/tmp/pickles.php');
$this->assertInstanceOf('Pickles\\Config', $config);
}
}

View file

@ -1,82 +0,0 @@
<?php
class ObjectTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"datasource" => "mysql",
],
"datasources" => [
"mysql" => [
"driver" => "pdo_mysql",
],
],
];
');
$config = Pickles\Config::getInstance('/tmp/pickles.php');
}
public static function tearDownAfterClass()
{
unlink('/tmp/pickles.php');
}
public function testConstructorWithoutObjects()
{
$object = new Pickles\Object();
$this->assertInstanceOf('Pickles\\Config', PHPUnit_Framework_Assert::readAttribute($object, 'config'));
}
public function testConstructorWithObjects()
{
$object = new Pickles\Object('cache');
$this->assertInstanceOf('Pickles\\Cache', $object->cache);
$object = new Pickles\Object(['cache', 'db']);
$this->assertInstanceOf('Pickles\\Cache', $object->cache);
$this->assertInstanceOf('Pickles\\Database', $object->db);
}
public function testGetInstanceWithoutClass()
{
$this->assertFalse(Pickles\Object::getInstance());
}
public function testProfiler()
{
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"datasource" => "mysql",
"profiler" => true,
"foo" => "bar",
],
"datasources" => [
"mysql" => [
"driver" => "pdo_mysql",
],
],
];
');
$config = Pickles\Config::getInstance('/tmp/pickles.php');
$object = new Pickles\Object();
}
}

View file

@ -1,66 +0,0 @@
<?php
class ProfilerTest extends PHPUnit_Framework_TestCase
{
public function testProfiler()
{
// Clears out any previous logging
Pickles\Profiler::report();
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"profiler" => true,
],
];
');
new Pickles\Config('/tmp/pickles.php');
Pickles\Profiler::log('i am a string');
Pickles\Profiler::log(['foo' => 'bar']);
Pickles\Profiler::log($this, 'testProfiler');
Pickles\Profiler::timer('swatch');
Pickles\Profiler::query('SELECT', ['foo' => 'bar'], ['results'], 1, 'EXPLAIN');
Pickles\Profiler::timer('swatch');
Pickles\Profiler::query('SELECT', ['foo' => 'bar'], ['results'], 1);
$report = Pickles\Profiler::report();
$this->assertEquals(7, count($report));
$this->assertEquals(7, count($report['logs']));
$this->assertEquals(5, count($report['logs'][0]));
$this->assertEquals('string', $report['logs'][0]['type']);
$this->assertEquals('i am a string', $report['logs'][0]['details']);
$this->assertEquals('array', $report['logs'][1]['type']);
$this->assertEquals(['foo' => 'bar'], $report['logs'][1]['details']);
$this->assertEquals('object', $report['logs'][2]['type']);
$this->assertEquals(['class' => 'ProfilerTest', 'method' => 'testProfiler()'], $report['logs'][2]['details']);
$this->assertEquals('timer', $report['logs'][3]['type']);
$this->assertEquals('swatch', $report['logs'][3]['details']['name']);
$this->assertEquals('start', $report['logs'][3]['details']['action']);
$this->assertEquals('database', $report['logs'][4]['type']);
$this->assertEquals('SELECT', $report['logs'][4]['details']['query']);
$this->assertEquals(['foo' => 'bar'], $report['logs'][4]['details']['parameters']);
$this->assertEquals(['results'], $report['logs'][4]['details']['results']);
$this->assertEquals(1, $report['logs'][4]['details']['execution_time']);
$this->assertEquals('EXPLAIN', $report['logs'][4]['details']['explain']);
$this->assertEquals('timer', $report['logs'][5]['type']);
$this->assertEquals('swatch', $report['logs'][5]['details']['name']);
$this->assertEquals('stop', $report['logs'][5]['details']['action']);
$this->assertEquals('database', $report['logs'][6]['type']);
$this->assertEquals('SELECT', $report['logs'][6]['details']['query']);
$this->assertEquals(['foo' => 'bar'], $report['logs'][6]['details']['parameters']);
$this->assertEquals(['results'], $report['logs'][6]['details']['results']);
$this->assertEquals(1, $report['logs'][6]['details']['execution_time']);
$this->assertFalse(isset($report['logs'][6]['details']['explain']));
}
}

View file

@ -1,282 +0,0 @@
<?php
namespace Pickles\App\Resources\v1
{
class resource extends \Pickles\Resource
{
public $https = [
'POST' => true,
];
public $auth = [
'DELETE' => true,
];
public $filter = [
'GET' => [
'foo' => 'trim',
'bar' => 'password_hash',
],
];
public $validate = [
'GET' => [
'missing',
'isBoolean' => ['filter:boolean' => 'Error'],
'isNotBoolean' => ['filter:boolean' => 'Error'],
'isEmail' => ['filter:email' => 'Error'],
'isNotEmail' => ['filter:email' => 'Error'],
'isFloat' => ['filter:float' => 'Error'],
'isNotFloat' => ['filter:float' => 'Error'],
'isInt' => ['filter:int' => 'Error'],
'isNotInt' => ['filter:int' => 'Error'],
'isIP' => ['filter:ip' => 'Error'],
'isNotIP' => ['filter:ip' => 'Error'],
'isURL' => ['filter:url' => 'Error'],
'isNotURL' => ['filter:url' => 'Error'],
'invalidRule' => ['filter' => 'Error'],
'lessThan' => ['length:<:10' => 'Error'],
'lessThanEqual' => ['length:<=:10' => 'Error'],
'equal' => ['length:==:10' => 'Error'],
'notEqual' => ['length:!=:10' => 'Error'],
'greaterThan' => ['length:>=:10' => 'Error'],
'greaterThanEqual' => ['length:>:10' => 'Error'],
'greaterLessThan' => ['length:><:10' => 'Error'],
'regex' => ['regex:/[a-z]+/' => 'Error'],
],
];
public function GET()
{
}
public function PUT()
{
return ['foo' => 'bar'];
}
public function ERROR()
{
throw new \Exception('Error');
}
}
}
namespace
{
class ResourceTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"namespace" => "",
"datasource" => "mysql",
],
"datasources" => [
"mysql" => [
"driver" => "pdo_mysql",
],
],
];
');
Pickles\Config::getInstance('/tmp/pickles.php');
}
public function testFilterAndValidate()
{
$response = json_encode([
'meta' => [
'status' => 400,
'message' => 'Missing or invalid parameters.',
'errors' => [
'missing' => ['The missing parameter is required.'],
'isNotBoolean' => ['Error'],
'isNotEmail' => ['Error'],
'isNotFloat' => ['Error'],
'isNotInt' => ['Error'],
'isNotIP' => ['Error'],
'isNotURL' => ['Error'],
'invalidRule' => ['Invalid filter, expecting boolean, email, float, int, ip or url.'],
'greaterLessThan' => ['Invalid operator, expecting <, <=, ==, !=, >= or >.'],
'regex' => ['Error'],
],
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'GET';
$_REQUEST['request'] = 'v1/resource/1';
$_GET = [
'foo' => ' bar ',
'bar' => 'unencrypted',
'isBoolean' => true,
'isNotBoolean' => 'invalid',
'isEmail' => 'foo@bar.com',
'isNotEmail' => 'nope',
'isFloat' => 1.234567890,
'isNotFloat' => 'five',
'isInt' => 22381,
'isNotInt' => 'pretzel',
'isIP' => '127.0.0.1',
'isNotIP' => 'home',
'isURL' => 'http://joshtronic.com',
'isNotURL' => 'doubleUdoubleUdoubleUdot',
'invalidRule' => 'invalid',
'lessThan' => '...',
'lessThanEqual' => '.......',
'equal' => '..........',
'notEqual' => '.......',
'greaterThan' => '............',
'greaterThanEqual' => '............',
'greaterLessThan' => '......',
'regex' => 'abc',
];
if (version_compare(PHP_VERSION, '5.5.0', '<'))
{
unset($_GET['bar']);
}
new Pickles\Router();
$this->assertEquals('bar', $_GET['foo']);
if (version_compare(PHP_VERSION, '5.5.0', '>='))
{
$this->assertFalse('unencrypted' == $_GET['bar']);
}
}
public function testHTTPS()
{
$response = json_encode([
'meta' => [
'status' => 400,
'message' => 'HTTPS is required.',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'POST';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
public function testPUT()
{
$response = json_encode([
'meta' => [
'status' => 200,
'message' => 'OK',
],
'response' => [
'foo' => 'bar',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'PUT';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
public function testMisconfiguredAuth()
{
$response = json_encode([
'meta' => [
'status' => 401,
'message' => 'Authentication is not configured properly.',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'DELETE';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
public function testMethodNotAllowed()
{
$response = json_encode([
'meta' => [
'status' => 405,
'message' => 'Method not allowed.',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'NOPE';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
public function testLowErrorCode()
{
$response = json_encode([
'meta' => [
'status' => 500,
'message' => 'Error',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'ERROR';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
public function testProfiler()
{
$this->expectOutputRegex('/"profiler":{/');
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"namespace" => "",
"datasource" => "mysql",
"profiler" => true,
],
"datasources" => [
"mysql" => [
"driver" => "pdo_mysql",
],
],
];
');
Pickles\Config::getInstance('/tmp/pickles.php');
$_SERVER['REQUEST_METHOD'] = 'PUT';
$_REQUEST['request'] = 'v1/resource/1';
new Pickles\Router();
}
}
}

View file

@ -1,101 +0,0 @@
<?php
namespace Pickles\App\Resources\v1
{
class router extends \Pickles\Resource
{
}
}
namespace
{
class RouterTest extends PHPUnit_Framework_TestCase
{
public function testServerError()
{
$response = json_encode([
'meta' => [
'status' => 500,
'message' => 'Undefined index: request',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"pickles" => [
"namespace" => "",
],
"datasources" => [],
];
');
Pickles\Config::getInstance('/tmp/pickles.php');
new Pickles\Router();
}
public function testNotFound()
{
$response = json_encode([
'meta' => [
'status' => 404,
'message' => 'Not Found.',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'GET';
$_REQUEST['request'] = 'v1/doesnotexist';
new Pickles\Router();
}
// We're just testing that the class can be loaded, not that it will
// work. That logic is off in ResourceTest
public function testFoundWithUID()
{
Pickles\Object::$instances = [];
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['SERVER_NAME'] = '127.0.0.1';
file_put_contents('/tmp/pickles.php', '<?php
$config = [
"environments" => [
"local" => "127.0.0.1",
"production" => "123.456.789.0",
],
"datasources" => [],
];
');
Pickles\Config::getInstance('/tmp/pickles.php');
$response = json_encode([
'meta' => [
'status' => 405,
'message' => 'Method not allowed.',
],
]);
$this->expectOutputString($response);
$_SERVER['REQUEST_METHOD'] = 'GET';
$_REQUEST['request'] = 'v1/router/1';
new Pickles\Router();
}
}
}

View file

@ -1,125 +0,0 @@
<?php
class StringTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerFormatPhoneNumber
*/
public function testFormatPhoneNumber($a, $b)
{
$this->assertEquals(Pickles\String::formatPhoneNumber($a), $b);
}
public function providerFormatPhoneNumber()
{
return [
['1234567890', '123-456-7890'],
['123 456 7890', '123-456-7890'],
['123.456.7890', '123-456-7890'],
['123_456_7890', '123-456-7890'],
['1234567890', '123-456-7890'],
['1234-56-7890', '123-456-7890'],
['(123) 456-7890', '123-456-7890'],
['1234567890 x1000', '123-456-7890x1000'],
['(123) 456-7890_x10.00', '123-456-7890x1000'],
];
}
public function testIsEmpty()
{
$this->assertTrue(Pickles\String::isEmpty(''));
$this->assertTrue(Pickles\String::isEmpty(' '));
$this->assertTrue(Pickles\String::isEmpty(false));
$this->assertTrue(Pickles\String::isEmpty(null));
$this->assertTrue(Pickles\String::isEmpty(true, false));
$this->assertFalse(Pickles\String::isEmpty(0));
$this->assertFalse(Pickles\String::isEmpty('foo'));
$this->assertFalse(Pickles\String::isEmpty(' bar '));
$this->assertFalse(Pickles\String::isEmpty(true));
}
public function testRandom()
{
$this->assertEquals(strlen(Pickles\String::random()), 8);
$this->assertEquals(strlen(Pickles\String::random(16)), 16);
$this->assertEquals(preg_match('/[a-z0-9]/', Pickles\String::random(32, true, true)), 1);
$this->assertEquals(preg_match('/[a-z]/', Pickles\String::random(32, true, false)), 1);
$this->assertEquals(preg_match('/[0-9]/', Pickles\String::random(32, false, true)), 1);
$this->assertEquals(preg_match('/[0-9]/', Pickles\String::random(32, true, false)), 0);
$this->assertEquals(preg_match('/[a-z]/', Pickles\String::random(32, false, true)), 0);
$this->assertEquals(preg_match('/[a-z0-9]/', Pickles\String::random(32, false, false)), 0);
}
public function testRandomSimilarFalse()
{
$this->assertRegExp('/[a-hj-np-z2-9]{8}/', Pickles\String::random(8, true, true, false));
}
/**
* @dataProvider providerTruncate
*/
public function testTruncate($a, $b, $c, $d)
{
$this->assertEquals(Pickles\String::truncate($a, $b, $c), $d);
}
public function providerTruncate()
{
return [
['foo bar', 3, true, '<span title="foo bar">foo&hellip;</span>'],
['foo bar', 3, false, 'foo&hellip;'],
['foo bar', 7, true, 'foo bar'],
['foo bar', 8, true, 'foo bar'],
];
}
/**
* @dataProvider providerUpperWords
*/
public function testUpperWords($a, $b)
{
$this->assertEquals(Pickles\String::upperWords($a), $b);
}
public function providerUpperWords()
{
return [
['foo bar', 'Foo Bar'],
['FOO BAR', 'Foo Bar'],
['fOO bAR', 'Foo Bar'],
['foo@bar.com', 'foo@bar.com'],
['FOO@BAR.COM', 'FOO@BAR.COM'],
];
}
/**
* @dataProvider providerGenerateSlug
*/
public function testGenerateSlug($a, $b)
{
$this->assertEquals($b, Pickles\String::generateSlug($a));
}
public function providerGenerateSlug()
{
return [
['TEST STRING', 'test-string'],
['Test String', 'test-string'],
['TEST STRING', 'test-string'],
['#! Test String', 'test-string'],
['-test--string-', 'test-string'],
];
}
public function testPluralize()
{
$this->assertEquals('test', Pickles\String::pluralize('test', 1, false));
$this->assertEquals('1 test', Pickles\String::pluralize('test', 1, true));
$this->assertEquals('tests', Pickles\String::pluralize('test', 2, false));
$this->assertEquals('2 tests', Pickles\String::pluralize('test', 2, true));
}
}

View file

@ -1,182 +0,0 @@
<?php
class TimeTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
date_default_timezone_set('GMT');
}
/**
* @dataProvider providerAge
*/
public function testAge($a, $b)
{
$this->assertEquals(Pickles\Time::age($a), $b);
}
public function providerAge()
{
$time = strtotime('-25 years');
return [
[date('Y-m-d', $time), '25'],
[date('m/d/Y', $time), '25'],
[date('r', $time), '25'],
['today', '0'],
['400 days ago', '1'],
[true, Pickles\Time::age('1969-12-31')],
];
}
public function testAgePastTime()
{
$this->assertEquals(18, Pickles\Time::age(date('Y-m-d', strtotime('-18 years'))));
}
public function testAgeFutureTime()
{
$this->assertEquals(-18, Pickles\Time::age(date('Y-m-d', strtotime('18 years'))));
}
public function testAgeWrongFormat()
{
$this->assertEquals(17, Pickles\Time::age(date('Ymd', strtotime('December 31st -18 years'))));
}
public function testAgoJustNow()
{
$this->assertEquals('just now', Pickles\Time::ago(Pickles\Time::timestamp()));
}
public function testAgoPastTimeSeconds()
{
$this->assertEquals('seconds ago', Pickles\Time::ago(strtotime('-30 seconds')));
}
public function testAgoPastTimeMinute()
{
$this->assertEquals('a minute ago', Pickles\Time::ago(strtotime('-1 minutes')));
}
public function testAgoPastTimeMinutes()
{
$this->assertEquals('5 minutes ago', Pickles\Time::ago(strtotime('-5 minutes')));
}
public function testAgoPastTimeHour()
{
$this->assertEquals('an hour ago', Pickles\Time::ago(strtotime('-1 hours')));
}
public function testAgoPastTimeHours()
{
$this->assertEquals('2 hours ago', Pickles\Time::ago(strtotime('-2 hours')));
}
public function testAgoPastTimeDay()
{
$this->assertEquals('a day ago', Pickles\Time::ago(strtotime('-1 days')));
}
public function testAgoPastTimeDays()
{
$this->assertEquals('2 days ago', Pickles\Time::ago(strtotime('-2 days')));
}
public function testAgoPastTimeWeek()
{
$this->assertEquals('a week ago', Pickles\Time::ago(strtotime('-1 weeks')));
}
public function testAgoPastTimeWeeks()
{
$this->assertEquals('2 weeks ago', Pickles\Time::ago(strtotime('-2 weeks')));
}
public function testAgoPastTimeMonth()
{
$this->assertEquals('a month ago', Pickles\Time::ago(strtotime('-1 months')));
}
public function testAgoPastTimeMonths()
{
$this->assertEquals('2 months ago', Pickles\Time::ago(strtotime('-2 months')));
}
public function testAgoPastTimeYear()
{
$this->assertEquals('a year ago', Pickles\Time::ago(strtotime('-1 years')));
}
public function testAgoPastTimeYears()
{
$this->assertEquals('2 years ago', Pickles\Time::ago(strtotime('-2 years')));
}
public function testAgoFutureTimeSeconds()
{
$this->assertEquals('seconds from now', Pickles\Time::ago(strtotime('+30 seconds')));
}
public function testAgoFutureTimeMinutes()
{
$this->assertEquals('5 minutes from now', Pickles\Time::ago(strtotime('+5 minutes')));
}
public function testAgoFutureTimeHours()
{
$this->assertEquals('an hour from now', Pickles\Time::ago(strtotime('+1 hour')));
}
public function testAgoFutureTimeDays()
{
$this->assertEquals('a day from now', Pickles\Time::ago(strtotime('+1 day')));
}
public function testAgoFutureTimeWeeks()
{
$this->assertEquals('a week from now', Pickles\Time::ago(strtotime('+1 week')));
}
public function testAgoFutureTimeMonths()
{
$this->assertEquals('a month from now', Pickles\Time::ago(strtotime('+1 month')));
}
public function testAgoFutureTimeYears()
{
$this->assertEquals('a year from now', Pickles\Time::ago(strtotime('+1 year')));
}
public function testTimestamp()
{
$this->assertEquals(gmdate('Y-m-d H:i:s'), Pickles\Time::timestamp());
}
public function testRoundUpHour()
{
$this->assertEquals('an hour ago', Pickles\Time::ago(strtotime('-59 minutes -55 seconds')));
}
public function testRoundUpDay()
{
$this->assertEquals('a day ago', Pickles\Time::ago(strtotime('-23 hours -55 minutes')));
}
public function testRoundUpWeek()
{
$this->assertEquals('a week ago', Pickles\Time::ago(strtotime('-6 days -23 hours')));
}
public function testRoundUpMonth()
{
$this->assertEquals('a month ago', Pickles\Time::ago(strtotime('-29 days')));
}
public function testRoundUpYear()
{
$this->assertEquals('a year ago', Pickles\Time::ago(strtotime('-364 days')));
}
}

View file

@ -1,20 +0,0 @@
<?php
require_once 'vendor/autoload.php';
$_SERVER['HTTP_HOST'] = 'testsite.com';
$_SERVER['SERVER_NAME'] = 'Test Server';
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
function setUpRequest($request, $method = 'GET')
{
$_SERVER['REQUEST_URI'] = '/' . $request;
$_SERVER['REQUEST_METHOD'] = $method;
$_REQUEST['request'] = $request;
}
`mysql -e 'TRUNCATE TABLE test.pickles;'`;
`mysql -e 'TRUNCATE TABLE test.mypickles;'`;
`mysql -e 'TRUNCATE TABLE test.users;'`;
`echo 'flush_all' | nc localhost 11211`;

View file

@ -0,0 +1,59 @@
<?php
require_once 'classes/Convert.php';
define('JSON_AVAILABLE', true);
class ConvertTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerToJSON
*/
public function testToJSON($a, $b)
{
$this->assertEquals(Convert::toJSON($a), $b);
}
public function providerToJSON()
{
$object = (object)'object';
$object->foo = 'foo';
$object->bar = 'bar';
return array(
array('', '""'),
array('foo', '"foo"'),
array(array('bar'), '["bar"]'),
array(array('foo', 'bar'), '["foo","bar"]'),
array(19810223, '19810223'),
array(array(1981, 02, 23), '[1981,2,23]'),
array(array('foo', 1981), '["foo",1981]'),
array(array('foo', array('bar')), '["foo",["bar"]]'),
array($object, '{"scalar":"object","foo":"foo","bar":"bar"}'),
array(true, 'true'),
array(false, 'false'),
array(null, 'null'),
);
}
/**
* @dataProvider providerArrayToXML
*/
public function testArrayToXML($a, $b, $c)
{
$this->assertEquals(Convert::arrayToXML($a, $b), $c);
}
public function providerArrayToXML()
{
return array(
array('foo', false, ''),
array(array('foo', 'bar'), false, '<0>foo</0><1>bar</1>'),
array(array('foo', 'bar'), true, "<0>foo</0>\n<1>bar</1>\n"),
array(array('foo' => 'bar'), false, '<foo>bar</foo>'),
array(array('children' => array('child' => array('foo', 'bar'))), false, '<children><child>foo</child><child>bar</child></children>'),
array(array('children' => array('child' => array('foo', 'bar'))), true, "<children>\n\t<child>foo</child>\n\t<child>bar</child>\n</children>\n"),
);
}
}
?>

View file

@ -0,0 +1,32 @@
<?php
require_once 'classes/Date.php';
class DateTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerAge
*/
public function testAge($a, $b)
{
$this->assertEquals(Date::age($a), $b);
}
public function providerAge()
{
ini_set('date.timezone', 'America/New_York');
$time = strtotime('-25 years');
return array(
array(date('Y-m-d', $time), '25'),
array(date('m/d/Y', $time), '25'),
array(date('r', $time), '25'),
array('today', '0'),
array('400 days ago', '1'),
array(true, Date::age('1969-12-31')),
);
}
}
?>

View file

@ -0,0 +1,112 @@
<?php
require_once 'classes/String.php';
class StringTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerFormatPhoneNumber
*/
public function testFormatPhoneNumber($a, $b)
{
$this->assertEquals(String::formatPhoneNumber($a), $b);
}
public function providerFormatPhoneNumber()
{
return array(
array('1234567890', '123-456-7890'),
array('123 456 7890', '123-456-7890'),
array('123.456.7890', '123-456-7890'),
array('123_456_7890', '123-456-7890'),
array('1234567890', '123-456-7890'),
array('1234-56-7890', '123-456-7890'),
array('(123) 456-7890', '123-456-7890'),
array('1234567890 x1000', '123-456-7890x1000'),
array('(123) 456-7890_x10.00', '123-456-7890x1000'),
);
}
/**
* @dataProvider providerGenerateGravatarHash
*/
public function testGenerateGravatarHash($a, $b)
{
$this->assertEquals(String::generateGravatarHash($a), $b);
}
public function providerGenerateGravatarHash()
{
return array(
array('foo@bar.com', 'f3ada405ce890b6f8204094deb12d8a8'),
array('FOO@BAR.COM', 'f3ada405ce890b6f8204094deb12d8a8'),
);
}
public function testIsEmpty()
{
$this->assertTrue(String::isEmpty(''));
$this->assertTrue(String::isEmpty(' '));
$this->assertTrue(String::isEmpty(false));
$this->assertTrue(String::isEmpty(null));
$this->assertTrue(String::isEmpty(true, false));
$this->assertFalse(String::isEmpty(0));
$this->assertFalse(String::isEmpty('foo'));
$this->assertFalse(String::isEmpty(' bar '));
$this->assertFalse(String::isEmpty(true));
}
public function testRandom()
{
$this->assertEquals(strlen(String::random()), 8);
$this->assertEquals(strlen(String::random(16)), 16);
$this->assertEquals(preg_match('/[A-Z0-9]/', String::random(32, true, true)), 1);
$this->assertEquals(preg_match('/[A-Z]/', String::random(32, true, false)), 1);
$this->assertEquals(preg_match('/[0-9]/', String::random(32, false, true)), 1);
$this->assertEquals(preg_match('/[0-9]/', String::random(32, true, false)), 0);
$this->assertEquals(preg_match('/[A-Z]/', String::random(32, false, true)), 0);
$this->assertEquals(preg_match('/[A-Z0-9]/', String::random(32, false, false)), 0);
}
/**
* @dataProvider providerTruncate
*/
public function testTruncate($a, $b, $c, $d)
{
$this->assertEquals(String::truncate($a, $b, $c), $d);
}
public function providerTruncate()
{
return array(
array('foo bar', 3, true, '<span title="foo bar" style="cursor:help">foo...</span>'),
array('foo bar', 3, false, 'foo...'),
array('foo bar', 7, true, 'foo bar'),
array('foo bar', 8, true, 'foo bar'),
);
}
/**
* @dataProvider providerUpperWords
*/
public function testUpperWords($a, $b)
{
$this->assertEquals(String::upperWords($a), $b);
}
public function providerUpperWords()
{
return array(
array('foo bar', 'Foo Bar'),
array('FOO BAR', 'Foo Bar'),
array('fOO bAR', 'Foo Bar'),
array('foo@bar.com', 'foo@bar.com'),
array('FOO@BAR.COM', 'FOO@BAR.COM'),
);
}
}
?>

View file

@ -1,57 +0,0 @@
DROP TABLE IF EXISTS pickles;
CREATE TABLE `pickles` (
`id` int(1) unsigned NOT NULL AUTO_INCREMENT,
`field1` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`field2` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field3` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field4` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field5` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_id` int(1) unsigned DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_id` int(1) unsigned DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_id` int(1) unsigned DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`is_deleted` tinyint(1) unsigned DEFAULT '0',
PRIMARY KEY (`id`),
KEY is_deleted (is_deleted)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
DROP TABLE IF EXISTS mypickles;
CREATE TABLE `mypickles` (
`id` int(1) unsigned NOT NULL AUTO_INCREMENT,
`field1` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`field2` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field3` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field4` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`field5` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_id` int(1) unsigned DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_id` int(1) unsigned DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_id` int(1) unsigned DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`is_deleted` tinyint(1) unsigned DEFAULT '0',
PRIMARY KEY (`id`),
KEY is_deleted (is_deleted)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
DROP TABLE IF EXISTS users;
CREATE TABLE `users` (
`id` int(1) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`level` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'USER',
`created_id` int(1) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_id` int(1) unsigned DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_id` int(1) unsigned DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`is_deleted` tinyint(1) unsigned DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO users (id, username, created_at) VALUES (1000000, 'test', NOW());

View file

@ -1,15 +0,0 @@
#!/bin/sh
VERSION=`phpenv version-name`
if [ "${VERSION}" = 'hhvm' ]
then
PHPINI=/etc/hhvm/php.ini
else
PHPINI=~/.phpenv/versions/$VERSION/etc/php.ini
echo "extension = memcache.so" >> $PHPINI
echo "extension = memcached.so" >> $PHPINI
echo "extension = redis.so" >> $PHPINI
fi

202
vendors/google/closure-compiler/COPYING vendored Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

292
vendors/google/closure-compiler/README vendored Normal file
View file

@ -0,0 +1,292 @@
/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Contents
//
The Closure Compiler performs checking, instrumentation, and
optimizations on JavaScript code. The purpose of this README is to
explain how to build and run the Closure Compiler.
The Closure Compiler requires Java 6 or higher.
http://www.java.com/
//
// Building The Closure Compiler
//
There are three ways to get a Closure Compiler executable.
1) Use one we built for you.
Pre-built Closure binaries can be found at
http://code.google.com/p/closure-compiler/downloads/list
2) Check out the source and build it with Apache Ant.
First, check out the full source tree of the Closure Compiler. There
are instructions on how to do this at the project site.
http://code.google.com/p/closure-compiler/source/checkout
Apache Ant is a cross-platform build tool.
http://ant.apache.org/
At the root of the source tree, there is an Ant file named
build.xml. To use it, navigate to the same directory and type the
command
ant jar
This will produce a jar file called "build/compiler.jar".
3) Check out the source and build it with Eclipse.
Eclipse is a cross-platform IDE.
http://www.eclipse.org/
Under Eclipse's File menu, click "New > Project ..." and create a
"Java Project." You will see an options screen. Give the project a
name, select "Create project from existing source," and choose the
root of the checked-out source tree as the existing directory. Verify
that you are using JRE version 6 or higher.
Eclipse can use the build.xml file to discover rules. When you
navigate to the build.xml file, you will see all the build rules in
the "Outline" pane. Run the "jar" rule to build the compiler in
build/compiler.jar.
//
// Running The Closure Compiler
//
Once you have the jar binary, running the Closure Compiler is straightforward.
On the command line, type
java -jar compiler.jar
This starts the compiler in interactive mode. Type
var x = 17 + 25;
then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux)
and "Enter" again. The Compiler will respond:
var x=42;
The Closure Compiler has many options for reading input from a file,
writing output to a file, checking your code, and running
optimizations. To learn more, type
java -jar compiler.jar --help
You can read more detailed documentation about the many flags at
http://code.google.com/closure/compiler/docs/gettingstarted_app.html
//
// Compiling Multiple Scripts
//
If you have multiple scripts, you should compile them all together with
one compile command.
java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
The Closure Compiler will concatenate the files in the order they're
passed at the command line.
If you need to compile many, many scripts together, you may start to
run into problems with managing dependencies between scripts. You
should check out the Closure Library. It contains functions for
enforcing dependencies between scripts, and a tool called calcdeps.py
that knows how to give scripts to the Closure Compiler in the right
order.
http://code.google.com/p/closure-library/
//
// Licensing
//
Unless otherwise stated, all source files are licensed under
the Apache License, Version 2.0.
-----
Code under:
src/com/google/javascript/rhino
test/com/google/javascript/rhino
URL: http://www.mozilla.org/rhino
Version: 1.5R3, with heavy modifications
License: Netscape Public License and MPL / GPL dual license
Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an
implementation of JavaScript for the JVM. The JavaScript parser and
the parse tree data structures were extracted and modified
significantly for use by Google's JavaScript compiler.
Local Modifications: The packages have been renamespaced. All code not
relevant to parsing has been removed. A JsDoc parser and static typing
system have been added.
-----
Code in:
lib/rhino
Rhino
URL: http://www.mozilla.org/rhino
Version: Trunk
License: Netscape Public License and MPL / GPL dual license
Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
Local Modifications: Minor changes to parsing JSDoc that usually get pushed
up-stream to Rhino trunk.
-----
Code in:
lib/args4j.jar
Args4j
URL: https://args4j.dev.java.net/
Version: 2.0.16
License: MIT
Description:
args4j is a small Java class library that makes it easy to parse command line
options/arguments in your CUI application.
Local Modifications: None.
-----
Code in:
lib/guava.jar
Guava Libraries
URL: http://code.google.com/p/guava-libraries/
Version: 13.0.1
License: Apache License 2.0
Description: Google's core Java libraries.
Local Modifications: None.
-----
Code in:
lib/jsr305.jar
Annotations for software defect detection
URL: http://code.google.com/p/jsr-305/
Version: svn revision 47
License: BSD License
Description: Annotations for software defect detection.
Local Modifications: None.
-----
Code in:
lib/jarjar.jar
Jar Jar Links
URL: http://jarjar.googlecode.com/
Version: 1.1
License: Apache License 2.0
Description:
A utility for repackaging Java libraries.
Local Modifications: None.
----
Code in:
lib/junit.jar
JUnit
URL: http://sourceforge.net/projects/junit/
Version: 4.10
License: Common Public License 1.0
Description: A framework for writing and running automated tests in Java.
Local Modifications: None.
---
Code in:
lib/protobuf-java.jar
Protocol Buffers
URL: http://code.google.com/p/protobuf/
Version: 2.4.1
License: New BSD License
Description: Supporting libraries for protocol buffers,
an encoding of structured data.
Local Modifications: None
---
Code in:
lib/ant.jar
lib/ant-launcher.jar
URL: http://ant.apache.org/bindownload.cgi
Version: 1.8.1
License: Apache License 2.0
Description:
Ant is a Java based build tool. In theory it is kind of like "make"
without make's wrinkles and with the full portability of pure java code.
Local Modifications: None
---
Code in:
lib/json.jar
URL: http://json.org/java/index.html
Version: JSON version 20090211
License: MIT license
Description:
JSON is a set of java files for use in transmitting data in JSON format.
Local Modifications: None
---
Code in:
tools/maven-ant-tasks-2.1.3.jar
URL: http://maven.apache.org
Version 2.1.3
License: Apache License 2.0
Description:
Maven Ant tasks are used to manage dependencies and to install/deploy to
maven repositories.
Local Modifications: None

Binary file not shown.

0
vendors/google/closure-compiler/grep vendored Normal file
View file