Compare commits

...

139 commits

Author SHA1 Message Date
Josh Sherman
8e88ffb440 Fixed composer.json and updated dependencies 2014-10-19 10:29:29 -04:00
Josh Sherman
11e4fee711 Switching the OAuth2 lib back again
Couldn't handle the fact that the errors were being echoed from the library
and not thrown or at the very least passed back so I could use them.
2014-10-19 10:26:15 -04:00
Josh Sherman
1ef2adae12 Added suggestions 2014-10-19 10:23:48 -04:00
Josh Sherman
a4242fd611 Updated dependencies 2014-10-19 09:31:01 -04:00
Josh Sherman
6414644f35 Shifting to that NoSQL life
Dropped the database abstraction layer, added a rudimentary MongoDB class,
dopped all of the interface classes for the OAuth2 library and updated the
dependencies to use @bshaffer's OAuth2 class as it has MongoDB support out of
the box.
2014-10-19 09:16:11 -04:00
Josh Sherman
76611eb7da Bye bye Model. 2014-10-18 12:54:33 -04:00
Josh Sherman
91579b3be4 Dropped Distance class
Moved off to joshtronic/php-distance
2014-10-17 07:26:29 -04:00
Josh Sherman
ecb075b344 Dropped Convert class
All it contained was an array to XML function. Moved off to
joshtronic/php-array2xml
2014-10-17 07:22:15 -04:00
Josh Sherman
ce45dc0dbe Dropped Sort class
Moved it off to joshtronic/php-sort
2014-10-17 07:18:47 -04:00
Josh Sherman
ea28dbbc5e Dropped file class
All it contained was a method to remove a directory recursively. Moved
files over to joshtronic/php-rmr (rm -r)
2014-10-17 07:15:06 -04:00
Josh Sherman
68b2f83379 Dropped Number class
Actually just moved it to joshtronic/php-ordinalindicator
2014-10-17 06:53:08 -04:00
Josh Sherman
9d5dac05c3 Dropped browser class
Actually moved it off to joshtronic/php-remoteaddr
2014-10-17 06:48:03 -04:00
Josh Sherman
b5b7cacc28 Merge branch '2.0' of git://github.com/picklesphp/pickles into 2.0 2014-10-16 21:12:00 -04:00
Josh Sherman
1aa39f3a8d Made refresh_token grant configurable 2014-10-16 19:25:57 -04:00
Josh Sherman
59af055f7e Merge branch 'master' into 2.0
Conflicts:
	composer.json
2014-10-16 10:40:52 -04:00
Josh Sherman
c824d3d5e6 Cleaned up the links a bit. 2014-10-16 10:40:11 -04:00
Josh Sherman
ba2729cb7f Merge branch '2.0' of github.com:picklesphp/pickles into 2.0 2014-10-16 10:34:12 -04:00
Josh Sherman
59777fe206 Update README.md 2014-10-16 10:29:49 -04:00
Josh Sherman
35af1f4037 Merge branch '2.0' of git://github.com/picklesphp/pickles into 2.0
Conflicts:
	src/OAuth2/SessionStorage.php
2014-10-16 07:32:06 -04:00
Josh Sherman
a40041acc6 Implemented refresh tokens
Right now it's hardcoded to always return a refresh token when you issue an
access token. Should think about making this an optional workflow or committing
to it being turned on indefinitely.
2014-10-16 07:30:32 -04:00
Josh Sherman
9e2e4f75f3 Tweaked schema some more. 2014-10-15 07:56:25 -04:00
Josh Sherman
f235f4a520 Tweaking the schema 2014-10-15 07:48:34 -04:00
Josh Sherman
08284b0f35 Fixed dependency version 2014-10-15 07:18:09 -04:00
Josh Sherman
32e7ae5f0f Switched oauth lib
Wasn't pleased to find that the new lib used the username as the primary key
across a bunch of tables. Not ideal IMO.
2014-10-15 07:08:39 -04:00
Josh Sherman
84231d9434 Swapped oauth lib 2014-10-14 22:24:46 -04:00
Josh Sherman
dc06f37320 Updated the interfaces and dropped oauth version
Seems the oauth lib's stable version is 3.2, dropped down from the 4 version
to that.
2014-10-14 22:05:52 -04:00
Josh Sherman
c244e02d46 Implementing storage interfaces 2014-10-14 07:11:03 -04:00
Josh Sherman
49a713eb35 Finished up password grant 2014-10-13 22:50:43 -04:00
Josh Sherman
ec14621e7c Tweaked unit tests 2014-10-13 21:46:34 -04:00
Josh Sherman
1cc1595e30 Updated dependencies 2014-10-13 21:30:57 -04:00
Josh Sherman
8e9c644822 Working out the routing 2014-10-13 21:27:19 -04:00
Josh Sherman
75596ed725 Moved to new namespace 2014-10-12 21:53:15 -04:00
Josh Sherman
2ec85c469b Working on that OAuth2 2014-10-12 21:20:48 -04:00
Josh Sherman
a834692235 Stubbed out storage classes 2014-10-11 07:40:09 -04:00
Josh Sherman
4a8378c6c2 Merge branch '2.0' of github.com:picklesphp/pickles into 2.0 2014-10-08 21:48:01 -04:00
Josh Sherman
cb4dac157d Updated dependencies 2014-10-08 21:42:42 -04:00
Josh Sherman
aa8c86e5c2 Working on oauth 2014-10-08 21:37:22 -04:00
Josh Sherman
840a68961c Cleaned up some double ;; 2014-10-07 12:10:46 -04:00
Josh Sherman
1599319478 Updated class name 2014-10-07 09:48:07 -04:00
Josh Sherman
846f75d0f3 Updated dependencies 2014-10-07 09:37:58 -04:00
Josh Sherman
8e60e9d553 Dropped date utility class
Only had one function and it was referencing the time class. Moved date tests
to the time tests because it seemed like a more comprehensive set of tests.
2014-10-07 09:33:21 -04:00
Josh Sherman
442e505cbf Merge branch 'master' into 2.0 2014-10-07 09:31:21 -04:00
Josh Sherman
de12028b35 Added oauth server as a dependency 2014-10-06 19:53:09 -04:00
Josh Sherman
c5d39db63b Brought Sort back into the repo
Decided that having a bunch of external dependencies would end up being more
trouble than it's worth
2014-10-06 18:27:53 -04:00
Josh Sherman
6c148c124e Added self-update to the travis build
Making this a standard thing in my travis scripts because it seems they let the
version go stale pretty regularly.
2014-10-04 11:10:59 -04:00
Josh Sherman
ee34d8aff9 Standardized the badges 2014-10-04 11:08:01 -04:00
Josh Sherman
77473c5b31 Dropped sort class from the core 2014-10-04 11:04:21 -04:00
Josh Sherman
e45e1251e1 Added auth test, cleaned up directory 2014-10-04 07:24:57 -04:00
Josh Sherman
a866a1a61b Merge branch 'master' into 2.0 2014-10-03 11:08:50 -04:00
Josh Sherman
2df1a5a162 Updated project name 2014-10-03 11:06:16 -04:00
Josh Sherman
ae98b67683 Cleaned up headers 2014-10-03 10:46:43 -04:00
Josh Sherman
84a785d4c9 Added Basic Auth functionality 2014-10-03 07:30:10 -04:00
Josh Sherman
89fc175701 Updated dependencies 2014-10-03 06:50:55 -04:00
Josh Sherman
e08c9a5ade Tweaked readme a bit 2014-10-03 06:43:44 -04:00
Josh Sherman
75951b90ef Updated dependencies 2014-10-03 06:41:42 -04:00
Josh Sherman
4c55c25f00 Cleared out instances 2014-10-03 06:33:55 -04:00
Josh Sherman
1a589efe12 Added config loading 2014-10-03 06:30:02 -04:00
Josh Sherman
908fff2193 Dropped php -a as it is hanging HHVM 2014-10-03 06:21:21 -04:00
Josh Sherman
823fe0759a Updated to only inject on non-HHVM
Saw someone else's .travis.yml that opted to not put those extension lines into
the INI when running on HHVM. Worth a shot!
2014-10-03 06:17:54 -04:00
Josh Sherman
ab6623d6fb Trying to debug hhvm
Added php -i to see where the php.ini file lives, the one I read about on
Github doesn't allow me to write to it.
2014-10-03 06:10:45 -04:00
Josh Sherman
c61a49f8a7 Fixed version check 2014-10-03 06:03:19 -04:00
Josh Sherman
e5270afed4 Dropped PHP 5.3 2014-10-02 23:40:56 -04:00
Josh Sherman
58e54bcf7b Updated lock file. 2014-10-02 22:56:42 -04:00
Josh Sherman
bbf392f45f Still tweaking towards HHVM 2014-10-02 22:01:59 -04:00
Josh Sherman
c7a6852a3d Working on script. 2014-10-02 21:55:16 -04:00
Josh Sherman
cd05384a6d Made script executable. 2014-10-02 21:49:28 -04:00
Josh Sherman
5b2f483fa5 Added script to set up extension
As per https://github.com/travis-ci/travis-ci/issues/2523 it appears that the
php.ini on hhvm is in a different place. Created a script to write the
extensions to the right php.ini based on the environment.
2014-10-02 21:47:01 -04:00
Josh Sherman
31f4c32fb0 Reworked test to check for PHP version
`password_hash` is PHP 5.5. Instead of adding the sanity check in the code,
I've opted to put it in the test to ditch the overhead of having to make that
check for every request.
2014-10-02 21:34:09 -04:00
Josh Sherman
1b365bcff0 Added root namespace to function
Tests were failing on 5.4 out on travis. Was barking about the function not
existing in the namespace. Hoping this resolves it.
2014-10-02 21:18:36 -04:00
Josh Sherman
5ce36537a6 Renamed bootstrap file 2014-10-02 21:11:23 -04:00
Josh Sherman
b1304a44d0 Merge branch 'master' of github.com:joshtronic/pickles 2014-10-02 21:05:20 -04:00
Josh Sherman
510b5d8edc Added new branches to test 2014-10-02 20:59:27 -04:00
Josh Sherman
d884f5a3df Finished up tests 2014-10-02 20:54:12 -04:00
Josh Sherman
4167d99623 Fixed deleting a file
Not sure when this became an issue, but attempting to delete a file that ends
with / will result in an error on OS X
2014-10-02 18:15:48 -04:00
Josh Sherman
90c4c53294 Fixed up the config again 2014-10-02 17:54:59 -04:00
Josh Sherman
c9ffe4c8bf Fixed config in tests 2014-10-02 17:44:00 -04:00
Josh Sherman
b48b73d064 Dropped SITE_PATH 2014-10-02 17:41:06 -04:00
Josh Sherman
a41ee7c2d2 Fixed config loading 2014-10-02 17:34:25 -04:00
Josh Sherman
df98b99440 Dropped uopz from travis config 2014-10-02 07:28:58 -04:00
Josh Sherman
c8d97aac26 Reworking database tests 2014-10-02 07:28:12 -04:00
Josh Sherman
07ed22c58f Fixed up resource class' tests 2014-10-02 07:02:29 -04:00
Josh Sherman
173136ddce Finished up Profiler and test coverage 2014-10-02 06:42:16 -04:00
Josh Sherman
9e65b2cfc2 Spruced up profiler, need to finish up tests 2014-10-01 22:08:11 -04:00
Josh Sherman
ee8bd63a08 Working on that new profiler
Gotta figure out what's going on with the database portion, it's going all
crazy on me.
2014-10-01 07:41:01 -04:00
Josh Sherman
506ff1fd45 Unit tests for the Object class 2014-09-30 22:24:10 -04:00
Josh Sherman
d4551d72a6 Evidently updated some dependencies 2014-09-30 18:07:49 -04:00
Josh Sherman
88a4375dd5 Wrapped up unit tests on Config class 2014-09-30 15:58:16 -04:00
Josh Sherman
2acd1b976e Updated dependencies 2014-09-30 15:06:53 -04:00
Josh Sherman
394c86d6ab Updated dependencies a bit 2014-09-30 09:49:08 -04:00
Josh Sherman
725d952192 Reworked config class, working on unit tests 2014-09-30 07:21:52 -04:00
Josh Sherman
52c8a730f3 Dropped pickles.ph made some assumptions
Wanted to be 100% reliant on the autoloader so I dropped the manual include of
pickles.php. Also moved some of the PHP ini logic out of here and baked in
some assumptions about environments and settings. Basically anything other than
"production" will set display errors to true, production is false.
2014-09-29 22:07:18 -04:00
Josh Sherman
1c974fc9ad Reworked config to be an arrayobject
Death to the mix of object variable and array. Also cleaned up the profiler
sanity checks as the variable will always be present now. Profiler is now an
all or nothing action.
2014-09-29 21:32:08 -04:00
Josh Sherman
f2d2f79a50 Update README.md 2014-09-29 17:58:38 -04:00
Josh Sherman
f2f1cbc166 Dropped validate test
All tests were moved to the RouterTest class
2014-09-28 21:51:06 -04:00
Josh Sherman
f62d12f35b Dropped dupe test 2014-09-28 21:50:28 -04:00
Josh Sherman
aea1bae3bf Working on tests for Resource 2014-09-28 21:49:49 -04:00
Josh Sherman
6c173cdc89 Working on tests for the Resource class.
Got a bunch more validation rules to port over.
2014-09-28 09:47:54 -04:00
Josh Sherman
3b8eddc7b5 Dropped status code method
The function `http_response_code` was added in PHP 5.4 which deprecated the
code I had written. Dropped functionality and tests and updated code to use the
new function.
2014-09-28 08:36:14 -04:00
Josh Sherman
da379d0849 Knocked out unit tests for the new Router 2014-09-28 08:24:02 -04:00
Josh Sherman
273af98883 Renamed bootstrap
Made it caps to go along with all of the existing test names
2014-09-28 07:43:28 -04:00
Josh Sherman
0cfc2c7979 Moved tests and updated to use namespaces 2014-09-28 07:31:02 -04:00
Josh Sherman
302f400dcb Removed some leftover debug code 2014-09-28 06:51:35 -04:00
Josh Sherman
372ba2812b Feeling frisky, added PHP 5.3 back to the tests
Curious if I'd be able to get back to PHP 5.3 compatibility to an extent.
2014-09-27 23:04:51 -04:00
Josh Sherman
59817fa5c4 That abridged README
Cut all the bullshit out so I could write out a better README once the system
is back to being stable and tested.
2014-09-27 22:55:12 -04:00
Josh Sherman
4af10e0fb6 Cleaning up a bit
The plan is to drop this file entirely and move the logic off to the config
class itself which will be instantiated by the router.
2014-09-27 22:36:03 -04:00
Josh Sherman
79f8da8c45 Namespaced the fuck out of Pickles 2014-09-27 22:28:15 -04:00
Josh Sherman
48c5289060 Moved files and added namespaces 2014-09-27 17:57:07 -04:00
Josh Sherman
8a1ac4fb47 Worked through basic authentication 2014-09-27 16:40:41 -04:00
Josh Sherman
718f8d64bb Working on basic auth and dropped log class
I personally haven't used it in years so I'm just getting to the mindset that
it's not a very useful piece of functionality. Nginx can easily log all of your
requests and any time I need to troubleshoot SQL (not all that often) I do it
directly without using the class.
2014-09-27 15:37:41 -04:00
Josh Sherman
bdb4ca8ff0 Renamed secure flag to https 2014-09-27 14:02:52 -04:00
Josh Sherman
53d8ab1137 Added powered by pickles header 2014-09-27 14:01:04 -04:00
Josh Sherman
20aff31b94 Moved validate logic into resource
I couldn't find a single scenario where I was using the Validate class in my
site level code. Dropped the overhead of calling a static class method multiple
times on a page by moving the logic in the Resource class. Also changed the
response to always return the errors as parameter => array. This allows a
developer to choose if they want to display one error or all of the errors.
2014-09-27 12:06:33 -04:00
Josh Sherman
50ea072929 Added password_hash filter
Defaulting to PASSWORD_DEFAULT, will expand to other options if the need arises.
2014-09-27 11:47:29 -04:00
Josh Sherman
3861bfc571 Dropped the filter class
Will revisit if there's ever a need to have access to it directly. Until then
it will find it in the Resource class.
2014-09-27 11:33:53 -04:00
Josh Sherman
141ac693cb Dropped response class
Seems pointless to have class that had a single method that basically contained
all of the shit that the resource already had and knew. Moved respond() method
to the response class and moved all of the response validation logic and errors
from the router to the response class.
2014-09-27 11:25:45 -04:00
Josh Sherman
a749c80d93 Added variable filtering and validation
Validation logic was existing but it was reworked to abstract out checking for
required fields initially and then sanity checks after the fact. Filters are
applied before validation but after checking existence. No support for _PUT and
_DELETE at the moment as those do not exist as super globals natively in PHP.
2014-09-27 08:13:05 -04:00
Josh Sherman
0ad0754726 Module -> Resource 2014-09-27 05:28:14 -04:00
Josh Sherman
824faffcc6 Stubbing out some new classes 2014-09-26 07:36:50 -04:00
Josh Sherman
7108c2c440 Dropped security groups 2014-09-26 07:34:11 -04:00
Josh Sherman
7f297b06f5 Put in a note about 404s 2014-09-26 07:33:32 -04:00
Josh Sherman
6097e29aef Cleaning up tests 2014-09-26 07:31:04 -04:00
Josh Sherman
bdb16009c1 Simplified the travis config a bit 2014-09-26 05:52:40 -04:00
Josh Sherman
2445f2f339 Added phpunit.xml
Was still typing in the full command to run phpunit from time to time. Can
now just run unit tests by running phpunit without any config flags
2014-09-26 05:49:49 -04:00
Josh Sherman
d7ad1d148e Dropped deprecated tests 2014-09-25 23:29:05 -04:00
Josh Sherman
39d5b8d40b Cleaned up unit tests
Wanted to get back to the point that the tests were running and not erroring.
2014-09-25 23:11:29 -04:00
Josh Sherman
ebc5584660 Merge branch '2.0' 2014-09-25 21:50:26 -04:00
Josh Sherman
26e913a3d9 Dropped Dynamic Class
Pretty sure we don't need it anymore. Could come back up in the future when an
API is returning URIs for static assets and they are being cached and it's not
being flushed correctly.
2014-09-25 21:48:13 -04:00
Josh Sherman
d5a2753c20 Reworked error handling. 2014-09-25 21:33:43 -04:00
Josh Sherman
69b14085b4 Module -> Resource
Dropping those dated naming conventions
2014-09-25 20:57:05 -04:00
Josh Sherman
94f46fc583 Controller -> Router, Display -> Response
Just gutting the brains of this thing.
2014-09-25 20:55:11 -04:00
Josh Sherman
17d19728e3 Update README.md
Tweaked buttons
2014-09-25 10:06:11 -04:00
Josh Sherman
98f03c2a76 Merge pull request #49 from waffle-iron/master
waffle.io Badge
2014-09-25 10:03:04 -04:00
Josh Sherman
b67269a202 Dropped that dirty dirty XML support. 2014-09-25 09:27:59 -04:00
Josh Sherman
2fa2b6ad03 Started gutting logic from the Controller and Display classes. 2014-09-24 23:57:39 -04:00
Josh Sherman
1381fd82a0 Dropped other classes we don't need any longer 2014-09-24 23:33:03 -04:00
Josh Sherman
e299d268c8 Setting headers and shit 2014-09-24 23:26:00 -04:00
Josh Sherman
0fdf2149be Dropped markup logic
Dropped the display / template rendering layer. API-first, converting to be a
lean mean data driven machine.
2014-09-24 23:24:36 -04:00
Josh Sherman
eba206556c Set up return structure. 2014-09-24 22:28:34 -04:00
Making GitHub Delicious.
aaa4a0fa16 add waffle.io badge 2014-09-24 07:57:17 -06:00
78 changed files with 3571 additions and 10108 deletions

View file

@ -5,16 +5,16 @@ php:
- 5.5
- 5.6
- hhvm
- hhvm-nightly
matrix:
allow_failures:
- php: hhvm
- php: hhvm-nightly
branches:
only:
- master
- 2.0
- 1.9
services:
- memcache
@ -22,24 +22,18 @@ services:
- redis
install:
- composer self-update
- composer install
- pecl install uopz
before_script:
- echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo -e "zend_$(cat ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini)" > ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "zend_extension = /home/travis/.phpenv/versions/$(php -r 'echo phpversion();')/lib/php/extensions/no-debug-zts-20100525/uopz.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "[uopz]" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "uopz.overloads=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- ./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 --stderr --colors --coverage-clover /home/travis/build/joshtronic/pickles/build/logs/clover.xml --bootstrap ./tests/bootstrap.php ./tests
- phpunit --coverage-clover /home/travis/build/joshtronic/pickles/build/logs/clover.xml
after_success:
- php vendor/bin/coveralls --config ../.coveralls.yml -v

View file

@ -1,65 +1,28 @@
# PICKLES [![Build Status](https://travis-ci.org/joshtronic/pickles.png?branch=master)](https://travis-ci.org/joshtronic/pickles) [![Coverage Status](https://coveralls.io/repos/joshtronic/pickles/badge.png)](https://coveralls.io/r/joshtronic/pickles) [![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a)
# Pickles
PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an
open source framework for rapid PHP development. PICKLES aims to be an “API
First” system for building APIs as well as AJAX/AJAJ-centric web applications.
[![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]
## Requirements
### Required Software
* Web server of your choice (nginx is highly recommended but Apache with
`mod_rewrite` will suffice)
* PHP 5.4+
Please note that PICKLES development is focused on the most recent stable
version of PHP (currently 5.5) but will maintain backwards compatibility with
the previous stable version. It may not be immediate, but when PHP 5.6 is
released compatibility for PHP 5.4 will be dropped in favor of modern
niceties.
For anyone stuck using PHP 5.3 is welcome to use [PICKLES v13.12.x][v13.12]
which at this time is still receiving bug fixes but will not be seeing any new
development by myself. Pull requests are welcome.
My rant about outdated server stacks can be found [on my blog][StackPost]
### Optional Software
* MySQL server with the `PDO_MYSQL` driver
* PostgreSQL server with the `PDO_PGSQL` driver
* SQLite 3 with the `PDO_SQLITE` driver
* Memcached server with the `Memcache` module
* `composer` if you want to compile LESS, SCSS or JS also necessary if you
want to use AYAH integration or run the test suite
* [UOPZ][UPOZ] if you want to be able to run the test suite
## 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 PICKLES source][MasterZip] (or clone the repository)
2. Place the code anywhere youd like (thats at least 2 directories up from
the root of your website). I recommend using `/usr/share/pickles[-vVERSION]`
3. Run `composer update`
4. A site already built in PICKLES can be found [here][HolidayAPI]
## TODO
* Bring the project's Wiki up to date
* Build an actual boilerplate site that would be included in this project
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.
## Thanks
Special thanks to [Geoff Oliver][GeoffOliver] for being a long time user and
contributor of PICKLES and to [Dean Jones][DeanJones] for coming up with the
PICKLES acronym.
contributor, [Justin Davis][JustinDavis] for romanticizing the v2 reimagining
and [Dean Jones][DeanJones] for helping to come up with the original PICKLES v1
acronym.
[DeanJones]: https://github.com/deanproxy
[GeoffOliver]: https://github.com/geoffoliver
[HolidayAPI]: https://github.com/gravityblvd/tools.gravityblvd.com
[MasterZip]: https://github.com/joshtronic/pickles/archive/master.zip
[StackPost]: http://joshtronic.com/2014/01/13/your-stack-is-outdated/#.UuVzI3n0A18
[UPOZ]: https://github.com/krakjoe/uopz
[v13.12]: https://github.com/joshtronic/pickles/tree/13.12
[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

View file

@ -1,29 +1,37 @@
{
"name": "joshtronic/pickles",
"description": "PHP Interface Collection of Killer Libraries to Enhance Stuff",
"type": "library",
"keywords": ["pickles", "framework", "rapid", "development", "mvc"],
"homepage": "https://github.com/joshtronic/pickles",
"license": "MIT",
"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",
"name": "Josh Sherman",
"email": "josh@gravityblvd.com",
"homepage": "http://joshtronic.com"
}
],
"repositories": [{
"type": "vcs",
"url": "https://github.com/joshtronic/php-ayah"
}],
"support": {
"issues": "https://github.com/joshtronic/pickles/issues",
"source": "https://github.com/joshtronic/pickles"
},
"minimum-stability" : "dev",
"require-dev": {
"mikey179/vfsStream": "1.3.0",
"phpunit/phpunit": "dev-master",
"satooshi/php-coveralls": "dev-master"
},
"require": {
"php": ">=5.4"
"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": {
"classmap": ["src/"]
"psr-4": {
"Pickles\\" : "src/"
}
}
}

1015
composer.lock generated

File diff suppressed because it is too large Load diff

14
phpunit.xml Normal file
View file

@ -0,0 +1,14 @@
<?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>

129
sql/oauth2.sql Normal file
View file

@ -0,0 +1,129 @@
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;

212
src/Config.php Normal file
View file

@ -0,0 +1,212 @@
<?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;
}
}

50
src/Mongo.php Normal file
View file

@ -0,0 +1,50 @@
<?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

@ -0,0 +1,84 @@
<?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

@ -0,0 +1,81 @@
<?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

@ -0,0 +1,56 @@
<?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()]);
}
}

141
src/OAuth2/Resource.php Normal file
View file

@ -0,0 +1,141 @@
<?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

@ -0,0 +1,26 @@
<?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

@ -0,0 +1,106 @@
<?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

@ -0,0 +1,20 @@
<?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,24 +1,23 @@
<?php
/**
* Object Class File for PICKLES
*
* PHP version 5
* Parent Object
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Object Class
*
* Every instantiated class in PICKLES should be extending this class. By doing
* 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.
*/
@ -40,65 +39,33 @@ class Object
public $config = null;
/**
* Instance of the Cache object
* Instance of the Mongo object
*
* @var object
*/
public $cache = null;
public $mongo = null;
/**
* Instance of the Database object
* Instance of the Redis object
*
* @var object
*/
public $db = null;
/**
* Profiler flag
*
* @var mixed
*/
public $profiler = false;
public $redis = null;
/**
* Constructor
*
* Establishes a Config instance for all children to enjoy
*/
public function __construct($objects = null)
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();
}
if ($objects)
{
if (!is_array($objects))
{
$objects = [$objects];
}
foreach ($objects as $object)
{
switch ($object)
{
case 'cache': $this->cache = Cache::getInstance(); break;
case 'db': $this->db = Database::getInstance(); break;
}
}
}
// Assigns the profiler flag
$this->profiler = (isset($this->config->pickles['profiler']) && $this->config->pickles['profiler'] != '' ? $this->config->pickles['profiler'] : false);
// @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->profiler === true || ((is_array($this->profiler) && in_array('objects', $this->profiler)) || stripos($this->profiler, 'objects') !== false))
if ($this->config['profiler'])
{
Profiler::log($this, '__construct');
}
@ -117,14 +84,10 @@ class Object
*/
public static function getInstance($class = false)
{
// In < 5.3 arguments must match in child, hence defaulting $class
// @todo Remove this, as we're no longer supporting 5.3
if ($class == false)
{
return false;
}
else
if ($class)
{
$class = 'Pickles\\' . $class;
if (!isset(self::$instances[$class]))
{
self::$instances[$class] = new $class();
@ -132,6 +95,8 @@ class Object
return self::$instances[$class];
}
return false;
}
/**
@ -140,7 +105,7 @@ class Object
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))
if ($this->config['profiler'])
{
Profiler::log($this, '__destruct');
}

196
src/Profiler.php Normal file
View file

@ -0,0 +1,196 @@
<?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;
}
}

387
src/Resource.php Normal file
View file

@ -0,0 +1,387 @@
<?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);
}
}

113
src/Router.php Normal file
View file

@ -0,0 +1,113 @@
<?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

@ -3,18 +3,17 @@
/**
* String Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* String Class
*
@ -63,7 +62,7 @@ class String
$string = strtolower(trim($string));
$string = preg_replace('/[^a-z0-9-]/', '-', $string);
$string = preg_replace('/-+/', '-', $string);
return trim($string, '-');;
return trim($string, '-');
}
// }}}

View file

@ -3,18 +3,17 @@
/**
* Time Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
* @package Pickles
*/
namespace Pickles;
/**
* Time Class
*
@ -116,9 +115,6 @@ class Time
* @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)
{

View file

@ -1,287 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 = [];
/**
* 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()
{
return 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;
}
/**
* Refresh
*
* Forces a browser refresh of the currently requested page.
*
* @static
*/
public static function refresh()
{
Browser::redirect($_SERVER['REQUEST_URI']);
}
/**
* 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 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);
}
}

View file

@ -1,222 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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();
// @todo Shouldn't need the isset() but Travis is failing some tests
if (isset($this->config->pickles['cache']) && $this->config->pickles['cache'])
{
$datasources = $this->config->pickles['cache'];
if (!is_array($datasources))
{
$datasources = [$datasources];
}
$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)
{
if (is_array($keys))
{
foreach ($keys as $index => $key)
{
$keys[$index] = strtoupper($this->namespace . $key);
}
}
else
{
$keys = strtoupper($this->namespace . $keys);
}
return $this->connection->get($keys);
}
/**
* 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, but
* have had great success caching data for a full day, hence defaulting the
* expiration to a full day.
*
* @param string $key key to set
* @param mixed $value value to set
* @param integer $expiration optional expiration, defaults to 1 day
* @return boolean status of writing the data to the key
*/
public function set($key, $value, $expire = Time::DAY)
{
$key = strtoupper($key);
return $this->connection->set(strtoupper($this->namespace . $key), $value, 0, $expire);
}
/**
* 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)
{
if (!is_array($keys))
{
$keys = [$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;
}
/**
* 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)
{
return $this->connection->increment(strtoupper($this->namespace . $key));
}
}

View file

@ -1,255 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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
*
* @var array
*/
public $data = [];
/**
* Constructor
*
* Calls the parent constructor and loads the passed file.
*/
public function __construct()
{
parent::__construct();
$filename = SITE_PATH . 'config.php';
$environments = false;
$environment = false;
$is_cli = !isset($_SERVER['REQUEST_METHOD']);
// Sanity checks the config file
if (file_exists($filename) && is_file($filename) && is_readable($filename))
{
require $filename;
}
// Checks that we have the config array
if (isset($config))
{
// 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
// @todo is checking for argc enough?
if ($is_cli && $_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 = [$hosts];
}
// Tries to determine the environment name
foreach ($hosts as $host)
{
if ($is_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;
}
}
}
}
}
}
// 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 we have an array convert to a string
if (is_array($this->data['pickles']['profiler']))
{
$this->data['pickles']['profiler'] = implode(',', $this->data['pickles']['profiler']);
}
}
else
{
$this->data['pickles']['profiler'] = false;
}
// Defaults expected PICKLES options to false
foreach (['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
*/
public 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 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];
}
}

View file

@ -1,433 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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();</code>
*/
class Controller 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();
// Generate a generic "site down" message if the site is set to be disabled
try
{
// @todo Clean this up to be just a single sanity check
if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'])
{
$custom_template = SITE_TEMPLATE_PATH . '__shared/maintenance.phtml';
if (file_exists($custom_template))
{
require_once $custom_template;
}
else
{
echo '
<h1>Down for Maintenance</h1>
<p>
' . $_SERVER['SERVER_NAME'] . ' is currently down for maintenance.
Please check back in a few minutes.
</p>
<p>Additionally, a custom maintenance template was not found.</p>
<hr>
<em>Powered by <a href="https://github.com/joshtronic/pickles">PICKLES</a></em>
';
}
throw new Exception();
}
// 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;
}
}
}
// Catches requests that aren't lowercase
$lowercase_request = strtolower($_REQUEST['request']);
if ($_REQUEST['request'] != $lowercase_request)
{
// @todo Rework the Browser class to handle the 301 (perhaps redirect301()) to not break other code
header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301);
throw new Exception();
}
// Grabs the requested page
$request = $_REQUEST['request'];
// Loads the module's information
$module_class = strtr($request, '/', '_');
$module_filename = SITE_MODULE_PATH . $request . '.php';
$module_exists = file_exists($module_filename);
// Attempts to instantiate the requested module
if ($module_exists)
{
if (class_exists($module_class))
{
$module = new $module_class;
}
}
// No module instantiated, load up a generic Module
if (!isset($module))
{
$module = new Module();
}
// 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'], true, 301);
throw new Exception();
}
elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false))
{
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301);
throw new Exception();
}
// Validates security level
if ($module->security)
{
$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, ['IS', 'HAS', 'BETWEEN']))
{
$security_check_class = $security_check_type;
}
unset($module_security['type']);
}
$module_security_levels = [];
// If there's a level(s) key use it
foreach (['level', 'levels'] as $security_level_key)
{
if (isset($module_security[$security_level_key]))
{
if (is_array($module_security[$security_level_key]))
{
$module_security_levels = 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)
{
// @todo Thinking of removing this?
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)
{
$is_authenticated = Security::hasLevel($module_security_levels);
}
break;
case 'IS':
if ($security_level_count)
{
$is_authenticated = Security::isLevel($module_security_levels);
}
break;
}
}
else
{
$is_authenticated = Security::isLevel($module->security);
}
if (!$is_authenticated)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// @todo Perhaps I could force a logout / redirect to the login page
Browser::status(401);
throw new Exception(json_encode([
'status' => 401,
'message' => 'You are not properly authenticated, try logging out and back in.',
]));
}
else
{
// Sets variable for the destination
$_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/';
// Redirect to login page
Browser::redirect('/login');
// Resolves testing error due to undefined $output
$output = '';
}
}
}
// Gets the profiler status
$profiler = $this->config->pickles['profiler'];
$profiler = $profiler === true || stripos($profiler, 'timers') !== false;
$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
// @todo Seems a bit redundant, refactor
if ($default_method == $role_method || method_exists($module, $default_method))
{
// Starts a timer before the module is executed
if ($profiler)
{
Profiler::timer('module ' . $default_method);
}
$valid_request = false;
$error_message = 'An unexpected error has occurred.';
// Determines if the request method is valid for this request
if ($module->method)
{
if (!is_array($module->method))
{
$module->method = [$module->method];
}
foreach ($module->method as $method)
{
if ($_SERVER['REQUEST_METHOD'] == $method)
{
$valid_request = true;
break;
}
}
if (!$valid_request)
{
// @todo Should probably utilize that AJAX flag to determine the type of return
$error_message = 'There was a problem with your request method.';
}
}
else
{
$valid_request = true;
}
$valid_form_input = true;
if ($valid_request && $module->validate)
{
$validation_errors = $module->__validate();
if ($validation_errors)
{
$error_message = implode(' ', $validation_errors);
$valid_form_input = false;
}
}
/**
* 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_form_input)
{
$module_return = $module->$default_method();
if (!is_array($module_return))
{
$module_return = $module->return;
}
else
{
$module_return = array_merge($module_return, $module->return);
}
}
// Stops the module timer
if ($profiler)
{
Profiler::timer('module ' . $default_method);
}
// Checks if we have any templates
$parent_template = $module->template;
$template_exists = $this->validateTemplates($module, $parent_template);
// No templates? 404 that shit
if (!$module_exists && !$template_exists)
{
Browser::status(404);
$_REQUEST['request'] = '__shared/404';
if (!$this->validateTemplates($module, $parent_template))
{
throw new Exception('
<h1>Not Found</h1>
<p>The requested URL /' . $request . ' was not found on this server.</p>
<p>Additionally, a custom error template was not found.</p>
<hr>
<em>Powered by <a href="https://github.com/joshtronic/pickles">PICKLES</a></em>
');
}
}
if (!isset($module_return))
{
$module_return = [
'status' => 'error',
'message' => $error_message,
];
}
// @todo Should simplify this, give Display direct acess to
// $module instead of all these variable assignment
$display = new Display();
$display->output = $module->output;
$display->templates = $module->template;
$display->module = $module_return;
// @todo Check for $module->meta variable first, then remove entirely when sites are updated
$display->meta = [
'title' => $module->title,
'description' => $module->description,
'keywords' => $module->keywords
];
}
// Starts a timer for the display rendering
if ($profiler)
{
Profiler::timer('display render');
}
// Renders the content
$output = $display->render();
// Stops the display timer
if ($profiler)
{
Profiler::timer('display render');
}
}
catch (Exception $e)
{
$output = $e->getMessage();
}
echo $output;
// Display the Profiler's report if the stars are aligned
if ($this->config->pickles['profiler'])
{
Profiler::report();
}
}
// @todo Document me
private function validateTemplates(&$module, $parent_template)
{
$templates = [
SITE_TEMPLATE_PATH . '__shared/' . $parent_template . '.phtml',
SITE_TEMPLATE_PATH . $_REQUEST['request'] . '.phtml',
];
$module->template = [];
$child_exists = file_exists($templates[1]);
if (file_exists($templates[0]) && $child_exists)
{
$module->template = $templates;
return true;
}
elseif ($child_exists)
{
$module->template = [$templates[1]];
return true;
}
return false;
}
}

View file

@ -1,122 +0,0 @@
<?php
/**
* Converter
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 =
* ['children' => [
* 'child' => [
* ['name' => 'Wendy Darling'],
* ['name' => 'John Darling'],
* ['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 : [$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;
}
// }}}
}

View file

@ -1,420 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
* @todo Drop driver, hardcode drivers based on the type
* @todo More assumptions for the datasource variables
*/
/**
* Database Class
*
* Database interaction all in one place. Allows for object reuse and contains
* functions to ease interacting with databases. Common assumptions about PDO
* attributes are baked in. Only support PDO.
*/
class Database extends Object
{
/**
* DSN format
*
* @access protected
* @var string
*/
protected $dsn;
/**
* PDO Attributes
*
* @access protected
* @var string
*/
protected $attributes = [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::NULL_EMPTY_STRING => true,
];
/**
* 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;
/**
* Get Instance
*
* Instantiates a new instance of the Database class or returns the
* previously instantiated copy.
*
* @static
* @param string $datasource_name name of the datasource
* @return object instance of the class
*/
public static function getInstance($datasource_name = false)
{
$config = Config::getInstance();
// Tries to load a datasource if one wasn't specified
if (!$datasource_name)
{
if (isset($config->pickles['datasource']))
{
$datasource_name = $config->pickles['datasource'];
}
elseif (is_array($config->datasources))
{
$datasources = $config->datasources;
foreach ($datasources as $name => $datasource)
{
if (isset($datasource['driver']))
{
$datasource_name = $name;
}
}
}
}
// Attempts to validate the datasource
if ($datasource_name)
{
if (!isset(self::$instances['Database'][$datasource_name]))
{
if (!isset($config->datasources[$datasource_name]))
{
throw new Exception('The specified datasource is not defined in the config.');
}
$datasource = $config->datasources[$datasource_name];
if (!isset($datasource['driver']))
{
throw new Exception('The specified datasource lacks a driver.');
}
$datasource['driver'] = strtolower($datasource['driver']);
// Checks the driver is legit and scrubs the name
switch ($datasource['driver'])
{
case 'pdo_mysql':
$attributes = [
'dsn' => 'mysql:host=[[hostname]];port=[[port]];unix_socket=[[socket]];dbname=[[database]]',
'port' => 3306,
];
break;
case 'pdo_pgsql':
$attributes = [
'dsn' => 'pgsql:host=[[hostname]];port=[[port]];dbname=[[database]];user=[[username]];password=[[password]]',
'port' => 5432,
];
break;
case 'pdo_sqlite':
$attributes = ['dsn' => 'sqlite:[[hostname]]'];
break;
default:
throw new Exception('Datasource driver "' . $datasource['driver'] . '" is invalid');
break;
}
// Instantiates our database class
$instance = new Database();
// Sets our database parameters
if (is_array($datasource))
{
$datasource = array_merge($attributes, $datasource);
foreach ($datasource as $variable => $value)
{
$instance->$variable = $value;
}
}
// Caches the instance for possible reuse later
self::$instances['Database'][$datasource_name] = $instance;
}
// Returns the instance
return self::$instances['Database'][$datasource_name];
}
return false;
}
/**
* 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)
{
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;
}
if (isset($this->username, $this->password, $this->database))
{
// Swaps out any variables with values in the DSN
$this->dsn = str_replace(
['[[hostname]]', '[[port]]', '[[socket]]', '[[username]]', '[[password]]', '[[database]]'],
[$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(['host=;', 'port=;', 'unix_socket=;'], '', $this->dsn);
// Attempts to establish a connection
$this->connection = new PDO($this->dsn, $this->username, $this->password, $this->attributes);
}
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
* @param boolean $force_slow optional, force slow query logging
* @return integer ID of the last inserted row or sequence number
*/
public function execute($sql, $input_parameters = null, $force_slow = false)
{
$this->open();
if (isset($this->config->pickles['logging']) && $this->config->pickles['logging'])
{
$loggable_query = $sql;
if ($input_parameters != null)
{
$loggable_query .= ' -- ' . json_encode($input_parameters);
}
Log::query($loggable_query);
}
$sql = trim($sql);
// Checks if the query is blank
if ($sql != '')
{
// Builds out stack trace for queries
$files = [];
$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) . '] */';
// Establishes if we're working on an EXPLAIN
if (Profiler::enabled('explains'))
{
$explain = preg_match('/^SELECT /i', $sql);
}
else
{
$explain = null;
}
// Executes a standard query
if ($input_parameters === null)
{
// Explains the query
if ($explain)
{
$explain = $this->fetch('EXPLAIN ' . $sql);
}
$start_time = microtime(true);
$this->results = $this->connection->query($sql);
}
// Executes a prepared statement
else
{
// Explains the query
if ($explain)
{
$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 ($duration >= 1 || $force_slow)
{
Log::slowQuery($duration . ' seconds: ' . $loggable_query);
}
// Logs the information to the profiler
if (Profiler::enabled('explains', 'queries'))
{
Profiler::logQuery($sql, $input_parameters, (isset($explain) ? $explain : false), $duration);
}
}
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

@ -1,40 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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);
}
}

View file

@ -1,204 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Display Class
*
* If you can see it then it probably happened in here.
*/
class Display extends Object
{
/**
* Return Type
*
* This class supports loading a PHP template, displaying JSON, XML and an
* RSS flavored XML. Inside your modules you can specify either a string or
* array. Possible values include "template", "json", "xml" and "rss".
* Default behavior is to try to load a template and fallback to displaying
* JSON. The "template" option always takes precedence when used with the
* other types.
*
* @var mixed string or array to determine how to return
*/
public $return = ['template', 'json'];
/**
* Templates
*
* Templates are found in the ./templates directory of your site. The
* template workflow is to load ./templates/__shared/index.phtml and you
* would set that template up to require $this->template, the path and
* filename for the module template (named based on the structure of the
* requested URI. Inside your module you can specify the basename of the
* parent template you would like to use or false to not use a parent
* template.
*
* @var string or boolean false the basename of the parent template
*/
public $templates = false;
/**
* Meta Data
*
* An array of meta data that you want exposed to the template. Currently
* you set the meta data from inside your module using the class variables
* title, description and keywords. The newer [preferred] method is to
* set an array in your module using the meta variable using title,
* description and keywords as the keys. You can also specify any other
* meta keys in the array that you would like to be exposed to your
* templates. The meta data is only used by TEMPLATE and RSS return types.
*/
public $meta = [];
/**
* Module Data
*
* Any data the module returns or is assigned inside of the module will
* be available here and exposed to the template.
*/
public $module = null;
public function render()
{
try
{
// Starts up the buffer so we can capture it
ob_start();
if (!is_array($this->return))
{
$this->return = [$this->return];
}
$return_json = $return_rss = $return_template = $return_xml = false;
foreach ($this->return as $return)
{
$variable = 'return_' . $return;
$$variable = true;
}
// Makes sure the return type is valid
if (!$return_json && !$return_rss && !$return_template && !$return_xml)
{
throw new Exception('Invalid return type.');
}
// Checks for the PHPSESSID in the query string
if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') === false)
{
// XHTML compliancy stuff
// @todo Wonder if this could be yanked now that we're in HTML5 land
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');
}
else
{
// Redirect so Google knows to index the page without the session ID
list($request_uri, $phpsessid) = explode('?PHPSESSID=', $_SERVER['REQUEST_URI'], 2);
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $request_uri);
throw new Exception('Requested URI contains PHPSESSID, redirecting.');
}
// @todo Derrive CSS and JS from _REQUEST['request'] no need to pass around
$loaded = false;
if ($return_template)
{
// Determines if we're using a custom class or not
$dynamic_class = (class_exists('CustomDynamic') ? 'CustomDynamic' : 'Dynamic');
$form_class = (class_exists('CustomForm') ? 'CustomForm' : 'Form');
$html_class = (class_exists('CustomHTML') ? 'CustomHTML' : 'HTML');
// Exposes some objects and variables to the local scope of the template
$this->request = $this->js_file = $_REQUEST['request'];
// @todo replace _ with - as it's more appropriate for CSS naming
$this->css_class = strtr($this->request, '/', '_');
// @todo Remove the magic $__variable when all sites are ported
$__config = $this->config;
$__css_class = $this->css_class;
$__js_file = $this->js_file;
$__meta = $this->meta;
$__module = $this->module;
$__dynamic = $this->dynamic = new $dynamic_class();
$__form = $this->form = new $form_class();
$__html = $this->html = new $html_class();
// Checks for the parent template and tries to load it
if ($this->templates)
{
$profiler = $this->config->pickles['profiler'];
$profiler = $profiler === true || stripos($profiler, 'timers') !== false;
// Starts a timer for the loading of the template
if ($profiler)
{
Profiler::timer('loading template');
}
// Assigns old variable
$required_template = $this->templates[0];
$__template = $this->template = end($this->templates);
$loaded = require_once $required_template;
// Stops the template loading timer
if ($profiler)
{
Profiler::timer('loading template');
}
}
}
if (!$loaded)
{
if ($return_json)
{
$pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false;
echo json_encode($this->module, $pretty);
}
elseif ($return_xml)
{
echo Convert::arrayToXML($this->module, isset($_REQUEST['pretty']));
}
}
// Grabs the buffer so we can massage it a bit
$buffer = ob_get_clean();
// Kills any whitespace and HTML comments in templates
if ($loaded)
{
// The BSA exception is because their system sucks and demands there be comments present
$buffer = preg_replace(['/^[\s]+/m', '/<!--(?:(?!BuySellAds).)+-->/U'], '', $buffer);
}
return $buffer;
}
catch (Exception $e)
{
return $e->getMessage();
}
}
}

View file

@ -1,143 +0,0 @@
<?php
/**
* Distance
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 && $pieces[1])
{
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);
}
// }}}
}

View file

@ -1,99 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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;
}
}

View file

@ -1,73 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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, ['.', '..']))
{
if (is_dir($directory . $file))
{
File::removeDirectory($directory . $file);
}
else
{
unlink($directory . $file);
}
}
}
rmdir($directory);
}
else
{
unlink($directory);
}
}
}

View file

@ -1,672 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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, ['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);
}
// }}}
// {{{ 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);
}
// }}}
// {{{ Textarea
/**
* 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 . '"' . $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 = [
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 = [
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 = [null => 'Day'];
$year_options = [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 (['month', 'day', 'year'] as $part)
{
$options = $part . '_options';
$selected = 'selected_' . $part;
$html .= ($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 = [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 = [
'area_code' => '',
'prefix' => '',
'line_number' => ''
];
}
else
{
$value = str_replace('-', '', $value);
$value = [
'area_code' => substr($value, 0, 3),
'prefix' => substr($value, 3, 3),
'line_number' => substr($value, 6)
];
}
$parts = [
'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;
}
// }}}
}

View file

@ -1,148 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 = ['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 = ['type' => $type];
}
}
if (is_array($attributes) && isset($attributes['label']))
{
if (isset($attributes['name']))
{
$label = $this->label(['for' => $attributes['name']], $attributes['label']);
}
else
{
$label = $this->label($attributes['label']);
}
unset($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) . '"';
}
}
}
$html .= '>';
if (!in_array($element, $this->self_closing))
{
$html .= $contents . '</' . $element . '>';
}
return $html;
}
}

View file

@ -1,151 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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)
{
$config = Config::getInstance();
if (isset($config->pickles['logging']) && $config->pickles['logging'])
{
$log_path = LOG_PATH . date('Y/m/d/', ($time == false ? time() : $time));
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);
}
}
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,293 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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
{
/**
* Page Title
*
* @var string, null by default
* @todo Abandon for $this->meta
*/
public $title = null;
/**
* Meta Description
*
* @var string, null by default
* @todo Abandon for $this->meta
*/
public $description = null;
/**
* Meta Keywords (comma separated)
*
* @var string, null by default
* @todo Abandon for $this->meta
*/
public $keywords = null;
/**
* Meta Data
*
* @var array
*/
public $meta = [
'title' => '',
'description' => '',
'keywords' => '',
];
/**
* Secure
*
* Whether or not the page should be loaded via SSL.
*
* @var boolean defaults to false
*/
public $secure = false;
/**
* Security Settings
*
* @var boolean, null by default
*/
public $security = null;
/**
* AJAX
*
* Whether or not the module is being called via AJAX. This determines if
* errors should be returned as JSON or if it should use the Error class
* which can be interrogated from within a template.
*
* @var boolean, false (not AJAX) by default
* @todo Doesn't seem to be in use, but I have it defined on Clipinary
* don't want to remove until I drop it else it would end up in the
* module return array.
*/
public $ajax = false;
/**
* Method
*
* Request methods that are allowed to access the module.
*
* @var string or array, null by default
*/
public $method = null;
/**
* Validate
*
* Variables to validate.
*
* @var array
*/
public $validate = [];
/**
* Template
*
* This is the parent template that will be loaded if you are using the
* 'template' return type in the Display class. Parent templates are found
* in ./templates/__shared and use the phtml extension.
*
* @var string, 'index' by default
*/
public $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']
*
* @var array
* @todo Rename __return so it's kinda obscured
* @todo Will need to update leaderbin and sndcrd to use new variable
*/
public $return = [];
/**
* Output
*
* What should the class render as output? This can be a string or an array
* containing either 'json', 'rss', 'template' or 'xml'. Default is to use
* templates and if the template is not present, fall back to JSON.
*
* @var mixed string or array
*/
public $output = ['template', 'json'];
/**
* 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()
* @param boolean $valiate optional flag to disable autorun validation
*/
public function __construct($autorun = false, $validate = true)
{
parent::__construct(['cache', 'db']);
if ($autorun === true)
{
if ($validate === true)
{
$errors = $this->__validate();
if ($errors !== false)
{
// @todo Fatal error perhaps?
exit('Errors encountered, this is a @todo for form validation when calling modules from inside of modules');
}
}
$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.
*/
public function __default()
{
}
/**
* Magic Setter Method
*
* Places undefined properties into the return array as part of the
* module's payload.
*
* @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
*
* Any variables not defined in this class are set in the return array and
* default to false if not defined there.
*
* @param string $name name of the variable requested
* @return mixed value of the variable or boolean false
*/
public function __get($name)
{
if (!isset($this->return[$name]))
{
return false;
}
else
{
return $this->return[$name];
}
}
/**
* Validate
*
* Internal validation for data passed to a Module. Grabs the super global
* based on the Module's request method and loops through the data using the
* Module's validation array (if present) sanity checking each variable
* against the rules.
*
* @return mixed boolean false if everything is fine or an array or errors
*/
public function __validate()
{
$errors = [];
if ($this->validate)
{
if (is_array($this->method))
{
$this->method = $this->method[0];
}
switch (strtoupper($this->method))
{
case 'GET':
$global = &$_GET;
break;
case 'POST':
$global = &$_POST;
break;
default:
$global = &$_REQUEST;
break;
}
foreach ($this->validate as $variable => $rules)
{
if (!is_array($rules) && $rules !== true)
{
$variable = $rules;
$rules = true;
}
if (isset($global[$variable]) && !String::isEmpty($global[$variable]))
{
if (is_array($rules))
{
$rule_errors = Validate::isValid($global[$variable], $rules);
if (is_array($rule_errors))
{
$errors = array_merge($errors, $rule_errors);
}
}
}
else
{
$errors[] = 'The ' . $variable . ' field is required.';
}
}
}
return $errors == [] ? false : $errors;
}
}

View file

@ -1,70 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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)
{
if (!in_array(($number % 100), [11, 12, 13]))
{
switch ($number % 10)
{
case 1:
$suffix = 'st';
break;
case 2:
$suffix = 'nd';
break;
case 3:
$suffix = 'rd';
break;
default:
$suffix = 'th';
break;
}
}
if ($superscript)
{
$suffix = '<sup>' . $suffix . '</sup>';
}
return $number . $suffix;
}
}

View file

@ -1,389 +0,0 @@
<?php
/**
* Profiler
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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
{
/**
* Profile
*
* Array of logged events
*
* @static
* @access private
* @var array
*/
private static $profile = [];
/**
* 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 = [];
/**
* 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 */)
{
$config = Config::getInstance();
$config = isset($config->pickles['profiler']) ? $config->pickles['profiler'] : false;
// Checks if we're set to boolean true
if ($config === true)
{
return true;
}
else
{
$types = func_get_args();
foreach ($types as $type)
{
if (stripos($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[] = [
'log' => $log,
'type' => $data_type,
'time' => $time,
'elapsed' => $time - $_SERVER['REQUEST_TIME_FLOAT'],
'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.
*
* @todo Thinking this should return the report and not necessarily echo it
*/
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 = $_SERVER['REQUEST_TIME_FLOAT'];
$peak_usage = self::formatSize(memory_get_peak_usage());
$end_time = self::$profile[count(self::$profile) - 1]['time']; // @todo No idea what though?
$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"><?= $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"><?= 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"><?= $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"><?= $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"><?= $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"><?= $entry['type']; ?></td>
<td><?= $entry['log']; ?></td>
<td style="text-align:right" nowrap="nowrap"><?= self::formatSize($entry['memory']); ?></td>
<td style="text-align:right" nowrap="nowrap"><?= number_format($entry['elapsed'] * 100, 3); ?> ms</td>
</tr>
<?php
}
?>
</table>
<?php
}
?>
</div>
<br><br>
<?php
self::$profile = [];
self::$queries = 0;
self::$timers = [];
}
/**
* 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
* @todo Probably can move this elsewhere and make it public
*/
private static function formatSize($filesize)
{
$units = ['bytes', 'kB', 'MB', 'GB'];
return number_format(round($filesize / pow(1024, ($i = floor(log($filesize, 1024)))), 2), 2) . ' ' . $units[$i];
}
}

View file

@ -1,342 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 = [];
/**
* 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
* @todo Transition away from this
*/
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 = ['P1ck73', 'Ju1C3'];
}
}
// Forces the variable to be an array
if (!is_array($salts))
{
$salts = [$salts];
}
// Loops through the salts, applies them and calculates the hash
$hash = $source;
foreach ($salts as $salt)
{
$hash = sha1($salt . $hash);
}
return $hash;
}
/**
* 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 = hash('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)
{
return is_int($access_level);
}
/**
* 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'] = [
'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'];
}
// Used to hit the database to determine the user's level, found it
// to be overkill and just opted for a simple logout.
else
{
Security::logout();
}
}
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;
}
}
}
}
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;
}
}
}
}
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;
}
}
}
return $between_level;
}
}

View file

@ -1,125 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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 an empty 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:
*/
class Session extends Object
{
/**
* Constructor
*
* All of our set up logic for the session in contained here. This class is
* initially instantiated from pickles.php. Non-file handlers need to be
* configured in the site's config. MySQL support was dropped in favor of
* in memory stores or simply relying on file based sessions. Why? Because
* using MySQL for sessions is very write intensive and having done it in
* the past I don't recommend it. If you run a single server, files are
* good enough if your volume is lower. Memcache[d] is fine if you don't
* mind logging all of your users off your site when you restart the
* service and/or you run out of memory for the process. Redis is the best
* choice as it can be configured to be persistent and lives in memory.
* This is assuming you don't just want to roll your own sessions, which is
* pretty damn easy as well.
*/
public function __construct()
{
if (isset($_SERVER['REQUEST_METHOD']))
{
parent::__construct();
// Sets up our configuration variables
if (isset($this->config->pickles['sessions']))
{
$session = $this->config->pickles['sessions'];
}
$datasources = $this->config->datasources;
$handler = 'files';
$datasource = false;
if (isset($session, $datasources[$session]))
{
$datasource = $datasources[$session];
$handler = $datasource['type'];
if ($handler != 'files')
{
if (isset($datasource['hostname'], $datasource['port']))
{
$host = ($handler != 'memcached' ? 'tcp://' : '')
. $datasource['hostname'] . ':' . $datasource['port'];
}
else
{
throw new Exception('You must provide both the hostname and port for the datasource.');
}
}
}
switch ($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;
case 'memcached':
ini_set('session.save_handler', 'memcached');
ini_set('session.save_path', $host);
break;
case 'redis':
$save_path = $host . '?weight=1';
// Database ignored by phpredis when this was coded
if (isset($datasource['database']))
{
$save_path .= '&database=' . $datasource['database'];
}
if (isset($datasource['prefix']))
{
$save_path .= '&prefix=' . $datasource['prefix'];
}
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', $save_path);
break;
case 'files':
ini_set('session.save_handler', 'files');
break;
}
// Don't start sessions for people without a user agent and bots.
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();
}
}
}
}

View file

@ -1,71 +0,0 @@
<?php
/**
* Sorting Utility Collection
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Sort Class
*
* I got tired of writing separate usort functions to sort by different array
* keys in the array, this class solves that.
*/
class Sort
{
/**
* Ascending
*
* Variable to utilize ascending sort
*
* @var integer
*/
const ASC = 'ASC';
/**
* Descending
*
* Variable to utilize descending sort
*
* @var integer
*/
const DESC = 'DESC';
/**
* Sort By
*
* Sorts an array by the specified column, optionally in either direction.
*
* @param string $field field to sort by
* @param array $array array to sort
* @param string $direction optional direction to sort
* @retun boolean true because sorting is done by reference
*/
public static function by($field, &$array, $direction = Sort::ASC)
{
usort($array, create_function('$a, $b', '
$a = $a["' . $field . '"];
$b = $b["' . $field . '"];
if ($a == $b)
{
return 0;
}
return ($a ' . ($direction == Sort::DESC ? '>' : '<') .' $b) ? -1 : 1;
'));
return true;
}
}

View file

@ -1,175 +0,0 @@
<?php
/**
* Validator
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Validate Class
*
* Validation layer that's used by the Modules to validate passed data. Handles
* single sanity checks against a variable so the validation itself can be used
* in other places in the system
*/
class Validate
{
/**
* Is Valid?
*
* Checks if a variable is valid based on the passed rules.
*
* @param mixed $value the value to be validated
* @param array $rules an array of rules (and messages) to validate with
* @return mixed boolean true if valid, array of errors if invalid
*/
public static function isValid($value, $rules)
{
$errors = [];
if (is_array($rules))
{
foreach ($rules as $rule => $message)
{
$rule = explode(':', $rule);
switch (strtolower($rule[0]))
{
// @todo case 'alpha':
// @todo case 'alphanumeric':
// @todo case 'date':
// {{{ Checks using filter_var()
case 'filter':
if (count($rule) < 2)
{
throw new Exception('Invalid validation rule, expected: "validate:boolean|email|float|int|ip|url".');
}
else
{
switch (strtolower($rule[1]))
{
case 'boolean':
case 'email':
case 'float':
case 'int':
case 'ip':
case 'url':
$filter = constant('FILTER_VALIDATE_' . strtoupper($rule[1]));
break;
default:
throw new Exception('Invalid filter, expecting boolean, email, float, int, ip or url.');
break;
}
if (!filter_var($value, $filter))
{
$errors[] = $message;
}
}
break;
// }}}
// {{{ Checks using strlen()
case 'length':
if (count($rule) < 3)
{
throw new Exception('Invalid validation rule, expected: "length:<|<=|==|!=|>=|>:integer".');
}
else
{
if (!filter_var($rule[2], FILTER_VALIDATE_INT))
{
throw new Exception('Invalid length value, expecting an integer.');
}
else
{
$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:
throw new Exception('Invalid operator, expecting <, <=, ==, !=, >= or >.');
break;
}
if (!$valid)
{
$errors[] = $message;
}
}
}
break;
// }}}
// @todo case 'range':
// {{{ Checks using preg_match()
case 'regex':
if (count($rule) < 3)
{
throw new Exception('Invalid validation rule, expected: "regex:is|not:string".');
}
else
{
$rule[1] = strtolower($rule[1]);
if (($rule[1] == 'is' && preg_match($rule[2], $value))
|| ($rule[1] == 'not' && !preg_match($rule[2], $value)))
{
$errors[] = $message;
}
}
break;
// }}}
}
}
}
return count($errors) ? $errors : true;
}
}

View file

@ -1,162 +0,0 @@
<?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 <josh@gravityblvd.com>
* @copyright Copyright 2007-2014, 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>
*/
// {{{ PICKLES Constants
// @todo Finish reworking constants to be part of the Config object
if (!defined('SITE_PATH'))
{
// Establishes our site paths, sanity check is to allow vfsStream in our tests
define('SITE_PATH', getcwd() . '/../');
}
if (!defined('SITE_CLASS_PATH'))
{
define('SITE_CLASS_PATH', SITE_PATH . 'classes/');
define('SITE_MODEL_PATH', SITE_PATH . 'models/');
// @todo The following 2 constants are being used in sites will need to update them before removing
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/');
}
// }}}
// {{{ 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);
// 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);
// }}}
// {{{ 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.
*
* @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';
$pickles_path = dirname(__FILE__) . '/';
$pickles_paths = [
'class' => $pickles_path . 'classes/',
];
// Path as the key, boolean value is whether ot not to convert back to hyphenated
$paths = [
$pickles_paths['class'] => 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;
}
spl_autoload_register('__autoload');
// }}}
// {{{ Loads the configuration file and sets any configuration options
// Loads the base config
$config = Config::getInstance();
// Injects PICKLES variables into the config
$config->data['pickles']['path'] = dirname(__FILE__) . '/';
// Configures any available PHP configuration options
if (is_array($config->php) && count($config->php))
{
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'] = 'home';
}
// }}}

View file

@ -0,0 +1,174 @@
<?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

@ -0,0 +1,82 @@
<?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

@ -0,0 +1,66 @@
<?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

@ -0,0 +1,282 @@
<?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

@ -0,0 +1,101 @@
<?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

@ -7,7 +7,7 @@ class StringTest extends PHPUnit_Framework_TestCase
*/
public function testFormatPhoneNumber($a, $b)
{
$this->assertEquals(String::formatPhoneNumber($a), $b);
$this->assertEquals(Pickles\String::formatPhoneNumber($a), $b);
}
public function providerFormatPhoneNumber()
@ -27,35 +27,35 @@ class StringTest extends PHPUnit_Framework_TestCase
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->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(String::isEmpty(0));
$this->assertFalse(String::isEmpty('foo'));
$this->assertFalse(String::isEmpty(' bar '));
$this->assertFalse(String::isEmpty(true));
$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(String::random()), 8);
$this->assertEquals(strlen(String::random(16)), 16);
$this->assertEquals(strlen(Pickles\String::random()), 8);
$this->assertEquals(strlen(Pickles\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('/[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]/', 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);
$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}/', String::random(8, true, true, false));
$this->assertRegExp('/[a-hj-np-z2-9]{8}/', Pickles\String::random(8, true, true, false));
}
/**
@ -63,7 +63,7 @@ class StringTest extends PHPUnit_Framework_TestCase
*/
public function testTruncate($a, $b, $c, $d)
{
$this->assertEquals(String::truncate($a, $b, $c), $d);
$this->assertEquals(Pickles\String::truncate($a, $b, $c), $d);
}
public function providerTruncate()
@ -81,7 +81,7 @@ class StringTest extends PHPUnit_Framework_TestCase
*/
public function testUpperWords($a, $b)
{
$this->assertEquals(String::upperWords($a), $b);
$this->assertEquals(Pickles\String::upperWords($a), $b);
}
public function providerUpperWords()
@ -100,7 +100,7 @@ class StringTest extends PHPUnit_Framework_TestCase
*/
public function testGenerateSlug($a, $b)
{
$this->assertEquals($b, String::generateSlug($a));
$this->assertEquals($b, Pickles\String::generateSlug($a));
}
public function providerGenerateSlug()
@ -116,10 +116,10 @@ class StringTest extends PHPUnit_Framework_TestCase
public function testPluralize()
{
$this->assertEquals('test', String::pluralize('test', 1, false));
$this->assertEquals('1 test', String::pluralize('test', 1, true));
$this->assertEquals('tests', String::pluralize('test', 2, false));
$this->assertEquals('2 tests', String::pluralize('test', 2, true));
$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));
}
}

182
tests/Pickles/TimeTest.php Normal file
View file

@ -0,0 +1,182 @@
<?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,38 +1,7 @@
<?php
uopz_overload(ZEND_EXIT, function(){});
ob_start();
@session_start();
require_once 'vendor/autoload.php';
$root = org\bovigo\vfs\vfsStream::setup('site');
if (!defined('SITE_PATH'))
{
define('SECURITY_LEVEL_USER', 10);
define('SECURITY_LEVEL_ADMIN', 20);
define('SITE_PATH', org\bovigo\vfs\vfsStream::url('site/'));
}
require_once 'src/pickles.php';
if (!file_exists(SITE_MODULE_PATH))
{
mkdir(SITE_MODULE_PATH, 0644);
}
if (!file_exists(SITE_TEMPLATE_PATH))
{
mkdir(SITE_TEMPLATE_PATH, 0644);
}
if (!file_exists(SITE_TEMPLATE_PATH . '__shared/'))
{
mkdir(SITE_TEMPLATE_PATH . '__shared/', 0644);
}
$_SERVER['HTTP_HOST'] = 'testsite.com';
$_SERVER['SERVER_NAME'] = 'Test Server';
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
@ -44,14 +13,6 @@ function setUpRequest($request, $method = 'GET')
$_REQUEST['request'] = $request;
}
function setUpConfig($config)
{
file_put_contents(
SITE_PATH . 'config.php',
'<?php $config = ' . var_export($config, true) . '; ?>'
);
}
`mysql -e 'TRUNCATE TABLE test.pickles;'`;
`mysql -e 'TRUNCATE TABLE test.mypickles;'`;
`mysql -e 'TRUNCATE TABLE test.users;'`;

View file

@ -1,105 +0,0 @@
<?php
class BrowserTest extends PHPUnit_Framework_TestCase
{
public function testGetInstance()
{
$this->assertInstanceOf('Browser', Browser::getInstance());
}
public function testSetAndGet()
{
$this->assertTrue(Browser::set('foo', 'bar'));
$this->assertEquals('bar', Browser::get('foo'));
}
public function testMissingVariable()
{
$this->assertFalse(Browser::get('missing'));
}
public function testGoHome()
{
Browser::goHome();
$this->assertTrue(in_array('Location: http://testsite.com/', xdebug_get_headers()));
}
public function testIsMobile()
{
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16';
$this->assertTrue(Browser::isMobile());
}
public function testIsNotMobile()
{
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.73.11 (KHTML, like Gecko) Version/7.0.1 Safari/537.73.11';
$this->assertFalse(Browser::isMobile());
}
public function testRemoteIPNone()
{
$this->assertFalse(Browser::remoteIP());
}
public function testRemoteIPRemoteAddress()
{
$_SERVER['REMOTE_ADDR'] = '1.2.3.4';
$this->assertEquals('1.2.3.4', Browser::remoteIP());
}
public function testRemoteIPHTTPXForwardedFor()
{
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.3.4.5';
$this->assertEquals('2.3.4.5', Browser::remoteIP());
}
public function testRemoteIPHTTPClientIP()
{
$_SERVER['HTTP_CLIENT_IP'] = '3.4.5.6';
$this->assertEquals('3.4.5.6', Browser::remoteIP());
}
public function testStatus1xx()
{
Browser::status(100);
$this->assertTrue(in_array('Status: 100 Continue', xdebug_get_headers()));
}
public function testStatus2xx()
{
Browser::status(200);
$this->assertTrue(in_array('Status: 200 OK', xdebug_get_headers()));
}
public function testStatus3xx()
{
Browser::status(300);
$this->assertTrue(in_array('Status: 300 Multiple Choices', xdebug_get_headers()));
}
public function testStatus4xx()
{
Browser::status(400);
$this->assertTrue(in_array('Status: 400 Bad Request', xdebug_get_headers()));
}
public function testStatus5xx()
{
Browser::status(500);
$this->assertTrue(in_array('Status: 500 Internal Server Error', xdebug_get_headers()));
}
public function testRefresh()
{
$_SERVER['REQUEST_URI'] = '/some/uri';
Browser::goHome();
Browser::refresh();
$this->assertTrue(in_array('Location: http://testsite.com/some/uri', xdebug_get_headers()));
}
}

View file

@ -1,87 +0,0 @@
<?php
class CacheTest extends PHPUnit_Framework_TestCase
{
private $config;
private $cache;
public function setUp()
{
$this->config = Config::getInstance();
$this->config->data['pickles']['cache'] = 'mc';
$this->config->data['datasources']['mc'] = [
'type' => 'memcache',
'hostname' => 'localhost',
'port' => 11211,
'namespace' => 'ns',
];
$this->cache = Cache::getInstance();
}
public function testGetInstance()
{
$this->assertInstanceOf('Cache', $this->cache);
}
public function testSetAndGet()
{
$key = String::random();
$value = String::random();
$this->cache->set($key, $value);
$this->assertEquals($value, $this->cache->get($key));
}
public function testSetAndGetMultiple()
{
$keys = $values = $expected = [];
for ($i = 0; $i < 5; $i++)
{
$keys[] = String::random();
$values[] = String::random();
}
foreach ($keys as $key => $key_name)
{
$value = $values[$key];
$expected['NS-' . strtoupper($key_name)] = $value;
$this->cache->set($key_name, $value);
}
$this->assertEquals($expected, $this->cache->get($keys));
}
public function testDelete()
{
$key = String::random();
$value = String::random();
$this->cache->set($key, $value);
$this->cache->delete($key);
$this->assertFalse($this->cache->get($key));
}
public function testIncrement()
{
$key = String::random();
$this->assertFalse($this->cache->increment($key));
$this->cache->set($key, 1);
$this->assertEquals(2, $this->cache->increment($key));
$this->assertEquals(3, $this->cache->increment($key));
$this->assertEquals(4, $this->cache->increment($key));
}
// Doesn't do much but test that the destructor doesn't explode
public function testDestructor()
{
$this->cache->__destruct();
}
}

View file

@ -1,161 +0,0 @@
<?php
class ConfigTest extends PHPUnit_Framework_TestCase
{
private $config;
public function setUp()
{
$this->config = Config::getInstance();
setupConfig([]);
$_SERVER['REQUEST_METHOD'] = 'GET';
}
public function testConfigProperty()
{
$config = new Config();
$this->assertTrue(PHPUnit_Framework_Assert::readAttribute($config, 'config'));
}
public function testInstanceOf()
{
$this->assertInstanceOf('Config', $this->config);
}
public function testUndefined()
{
$this->assertFalse($this->config->undefined);
}
public function testDefinedEnvironment()
{
setUpConfig([
'environment' => 'local',
]);
$config = new Config();
$this->assertEquals('local', $config->environment);
}
public function testMultipleEnvironmentsByIP()
{
setUpConfig([
'environments' => [
'local' => '127.0.0.1',
'prod' => '123.456.789.0',
],
]);
$config = new Config();
$this->assertEquals('local', $config->environment);
}
public function testMultipleEnvironmentsByRegex()
{
setUpConfig([
'environments' => [
'local' => '/^local\.testsite\.com$/',
'prod' => '/^testsite\.com$/',
],
]);
$config = new Config();
$this->assertEquals('prod', $config->environment);
}
public function testCLIEnvironment()
{
unset($_SERVER['REQUEST_METHOD']);
$_SERVER['argv'][1] = 'prod';
setUpConfig([
'environments' => [
'local' => '127.0.0.1',
'prod' => '123.456.789.0',
],
]);
$config = new Config();
$this->assertEquals('prod', $config->environment);
}
/**
* @expectedException Exception
* @expectedExceptionMessage You must pass an environment (e.g. php script.php <environment>)
*/
public function testCLIMissingEnvironment()
{
unset($_SERVER['REQUEST_METHOD']);
$_SERVER['argc'] = 1;
setUpConfig(['environments' => []]);
$config = new Config();
}
public function testProfiler()
{
setUpConfig([
'environment' => 'local',
'pickles' => ['profiler' => true],
]);
$config = new Config();
$this->assertTrue($config->pickles['profiler']);
}
public function testProfilerArray()
{
setUpConfig([
'environment' => 'local',
'pickles' => ['profiler' => ['objects', 'timers']],
]);
$config = new Config();
$this->assertEquals('objects,timers', $config->pickles['profiler']);
}
public function testSecurityConstant()
{
setUpConfig([
'environment' => 'local',
'security' => ['levels' => [10 => 'level']],
]);
$config = new Config();
$this->assertEquals(10, SECURITY_LEVEL_USER);
}
/**
* @expectedException Exception
* @expectedExceptionMessage The constant SECURITY_LEVEL_LEVEL is already defined
*/
public function testSecurityConstantAlreadyDefined()
{
setUpConfig([
'environment' => 'local',
'security' => ['levels' => [10 => 'level']],
]);
$config = new Config();
$this->assertEquals(10, SECURITY_LEVEL_USER);
}
// This test is just for coverage
public function testConfigArrayMissing()
{
file_put_contents(SITE_PATH . 'config.php', '');
new Config();
}
}

View file

@ -1,358 +0,0 @@
<?php
class ControllerTest extends PHPUnit_Framework_TestCase
{
private $config;
public function setUp()
{
$this->config = Config::getInstance();
$this->config->data['pickles']['disabled'] = false;
$this->config->data['pickles']['profiler'] = false;
$this->config->data['security']['levels'][10] = 'USER';
$this->config->data['security']['levels'][20] = 'ADMIN';
setUpRequest('home');
$module = '<?php class home extends Module { }';
file_put_contents(SITE_MODULE_PATH . 'home.php', $module);
}
public function testSiteDown()
{
$this->config->data['pickles']['disabled'] = true;
$this->expectOutputRegex('/Test Server is currently down for maintenance/');
new Controller();
}
public function testCustomSiteDown()
{
$this->config->data['pickles']['disabled'] = true;
file_put_contents(SITE_TEMPLATE_PATH . '__shared/maintenance.phtml', '<h1>Custom Down for Maintenance</h1>');
new Controller();
$this->expectOutputRegex('/<h1>Custom Down for Maintenance<\/h1>/');
}
public function testAttributesInURI()
{
setUpRequest('home/id:123');
new Controller();
$this->assertEquals(123, Browser::get('id'));
setUpRequest('home/id:456/foo:bar');
new Controller();
// Compensates for 2 empty template executions of the Controller
$this->expectOutputString('[][]');
$this->assertEquals(456, Browser::get('id'));
$this->assertEquals('bar', Browser::get('foo'));
}
public function testUpperCaseURI()
{
setUpRequest('TESTING');
new Controller();
$this->assertTrue(in_array('Location: /testing', xdebug_get_headers()));
}
public function testForceSecure()
{
setUpRequest('secure');
$module = '<?php class secure extends Module { public $secure = true; }';
file_put_contents(SITE_MODULE_PATH . 'secure.php', $module);
new Controller();
$this->assertTrue(in_array('Location: https://testsite.com/secure', xdebug_get_headers()));
}
public function testForceInsecure()
{
setUpRequest('insecure');
$_SERVER['HTTPS'] = 'on';
$module = '<?php class insecure extends Module { public $secure = false; }';
file_put_contents(SITE_MODULE_PATH . 'insecure.php', $module);
new Controller();
$this->assertTrue(in_array('Location: http://testsite.com/insecure', xdebug_get_headers()));
}
public function testNotAuthenticated()
{
setUpRequest('notauth');
$module = '<?php class notauth extends Module { public $security = SECURITY_LEVEL_USER; }';
file_put_contents(SITE_MODULE_PATH . 'notauth.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testSecurityArray()
{
setUpRequest('securityarray');
$module = '<?php class securityarray extends Module { public $security = [SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]; }';
file_put_contents(SITE_MODULE_PATH . 'securityarray.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testSecurityArrayTypeString()
{
setUpRequest('securityarraytypestring');
$module = '<?php class securityarraytypestring extends Module { public $security = ["type" => "IS", "level" => SECURITY_LEVEL_USER]; }';
file_put_contents(SITE_MODULE_PATH . 'securityarraytypestring.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testSecurityArrayTypeArray()
{
setUpRequest('securityarraytypearray');
$module = '<?php class securityarraytypearray extends Module { public $security = ["type" => "IS", "level" => [SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]]; }';
file_put_contents(SITE_MODULE_PATH . 'securityarraytypearray.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testSecurityArrayTypeBetween()
{
setUpRequest('securityarraytypebetween');
$module = '<?php class securityarraytypebetween extends Module { public $security = ["type" => "BETWEEN", "levels" => [SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]]; }';
file_put_contents(SITE_MODULE_PATH . 'securityarraytypebetween.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testSecurityArrayTypeHas()
{
setUpRequest('securityarraytypehas');
$module = '<?php class securityarraytypehas extends Module { public $security = ["type" => "HAS", "level" => SECURITY_LEVEL_USER]; }';
file_put_contents(SITE_MODULE_PATH . 'securityarraytypehas.php', $module);
new Controller();
// Compensates for an empty template due to exit() being skipped
$this->expectOutputString('[]');
$this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers()));
}
public function testNotAuthenticatedPOST()
{
setUpRequest('notauthpost', 'POST');
$module = '<?php class notauthpost extends Module { public $security = SECURITY_LEVEL_USER; }';
file_put_contents(SITE_MODULE_PATH . 'notauthpost.php', $module);
new Controller();
$this->expectOutputRegex('/You are not properly authenticated/');
}
public function testAuthenticated()
{
setUpRequest('auth');
$module = '<?php class auth extends Module { '
. 'public $security = SECURITY_LEVEL_USER;'
. 'public function __default() { return ["foo" => "bar"]; }'
. '}';
file_put_contents(SITE_MODULE_PATH . 'auth.php', $module);
Security::login(1, 10, 'USER');
new Controller();
$this->expectOutputString('{"foo":"bar"}');
}
public function testRoleDefaultMethod()
{
setUpRequest('rolemethod');
$module = '<?php class rolemethod extends Module { '
. 'public $security = SECURITY_LEVEL_USER;'
. 'public function __default() { return ["foo" => "bar"]; }'
. 'public function __default_USER() { return ["user" => "me"]; }'
. '}';
file_put_contents(SITE_MODULE_PATH . 'rolemethod.php', $module);
Security::login(1, 10, 'USER');
new Controller();
$this->expectOutputString('{"user":"me"}');
}
public function testValidRequestMethod()
{
setUpRequest('validrequestmethod');
$module = '<?php class validrequestmethod extends Module { '
. 'public $method = "GET";'
. 'public function __default() { return ["foo" => "bar"]; }'
. '}';
file_put_contents(SITE_MODULE_PATH . 'validrequestmethod.php', $module);
new Controller();
$this->expectOutputString('{"foo":"bar"}');
}
public function testInvalidRequestMethod()
{
setUpRequest('invalidrequestmethod');
$module = '<?php class invalidrequestmethod extends Module { '
. 'public $method = "POST";'
. 'public function __default() { return ["foo" => "bar"]; }'
. '}';
file_put_contents(SITE_MODULE_PATH . 'invalidrequestmethod.php', $module);
new Controller();
$this->expectOutputString('{"status":"error","message":"There was a problem with your request method."}');
}
public function testValidationErrors()
{
setUpRequest('validationerrors');
$module = '<?php class validationerrors extends Module { '
. 'public $validate = ["test"];'
. 'public function __default() { return ["foo" => "bar"]; }'
. '}';
file_put_contents(SITE_MODULE_PATH . 'validationerrors.php', $module);
new Controller();
$this->expectOutputString('{"status":"error","message":"The test field is required."}');
}
public function testError404()
{
setUpRequest('fourohfour');
new Controller();
$this->assertTrue(in_array('Status: 404 Not Found', xdebug_get_headers()));
$this->expectOutputRegex('/<h1>Not Found<\/h1>/');
}
public function testCustomError404()
{
setUpRequest('customfourohfour');
file_put_contents(SITE_TEMPLATE_PATH . '__shared/404.phtml', '<h1>Custom Not Found</h1>');
new Controller();
$this->assertTrue(in_array('Status: 404 Not Found', xdebug_get_headers()));
$this->expectOutputRegex('/<h1>Custom Not Found<\/h1>/');
}
public function testProfilerOutput()
{
$this->config->data['pickles']['profiler'] = true;
$this->expectOutputRegex('/id="pickles-profiler"/');
new Controller();
}
public function testTwoValidTemplates()
{
$this->config->data['pickles']['profiler'] = true;
setUpRequest('validtemplates');
$module = '<?php class validtemplates extends Module { }';
file_put_contents(SITE_MODULE_PATH . 'validtemplates.php', $module);
$child_template = SITE_TEMPLATE_PATH . 'validtemplates.phtml';
file_put_contents($child_template, '<div>child template</div>');
// Vim syntax highlighting borks unless ----v
$child = '<?php require $this->template; ?' . '>' . "\n";
$html = <<<HTML
<!doctype html>
<html>
<body>
<h1>parent template</h1>
{$child}
</body>
</html>
HTML;
file_put_contents(SITE_TEMPLATE_PATH . '__shared/index.phtml', $html);
new Controller();
$this->expectOutputRegex('/^<!doctype html>
<html>
<body>
<h1>parent template<\/h1>
<div>child template<\/div>
<\/body>
<\/html>.+<style>/');
}
}

View file

@ -1,27 +0,0 @@
<?php
class ConvertTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerArrayToXML
*/
public function testArrayToXML($a, $b, $c)
{
$this->assertEquals(Convert::arrayToXML($a, $b), $c);
}
public function providerArrayToXML()
{
return [
['foo', false, ''],
[['foo', 'bar'], false, '<0>foo</0><1>bar</1>'],
[['foo', 'bar'], true, "<0>foo</0>\n<1>bar</1>\n"],
[['foo' => 'bar'], false, '<foo>bar</foo>'],
[['foo' => 'b & r'], false, '<foo><![CDATA[b & r]]></foo>'],
[['children' => ['child' => ['foo', 'bar']]], false, '<children><child>foo</child><child>bar</child></children>'],
[['children' => ['child' => ['foo & bar']]], false, '<children><child><![CDATA[foo & bar]]></child></children>'],
[['children' => ['child' => ['foo', 'bar']]], true, "<children>\n\t<child>foo</child>\n\t<child>bar</child>\n</children>\n"],
];
}
}

View file

@ -1,193 +0,0 @@
<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
public function testGetInstanceFalse()
{
$this->assertFalse(Database::getInstance());
}
/**
* @expectedException Exception
* @expectedExceptionMessage The specified datasource is not defined in the config.
*/
public function testGetInstanceDatasourceNotDefined()
{
$config = Config::getInstance();
$config->data['pickles']['datasource'] = 'bad';
Database::getInstance();
}
/**
* @expectedException Exception
* @expectedExceptionMessage The specified datasource lacks a driver.
*/
public function testGetInstanceDatasourceLacksDriver()
{
$config = Config::getInstance();
$config->data['datasources'] = [
'bad' => [
'type' => 'mysql',
],
];
$this->assertInstanceOf('Database', Database::getInstance());
}
/**
* @expectedException Exception
* @expectedExceptionMessage There was an error loading the database configuration.
*/
public function testOpenConfigError()
{
$config = Config::getInstance();
$config->data['datasources'] = [
'bad' => [
'type' => 'mysql',
'driver' => 'pdo_mysql',
'database' => 'test',
],
];
$db = Database::getInstance();
$db->open();
}
public function testGetInstanceDatasourcesArray()
{
$config = Config::getInstance();
$config->data['datasources'] = [
'mysql' => [
'type' => 'mysql',
'driver' => 'pdo_mysql',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'test',
],
];
$this->assertInstanceOf('Database', Database::getInstance());
}
// Also tests the datasource being missing and selecting the first one
public function testGetInstanceMySQL()
{
$config = Config::getInstance();
unset($config->data['pickles']['datasource']);
$this->assertInstanceOf('Database', Database::getInstance());
}
public function testOpenMySQL()
{
$config = Config::getInstance();
$config->data['pickles']['datasource'] = 'mysql';
$db = Database::getInstance();
$db->open();
}
public function testExecute()
{
$db = Database::getInstance();
$this->assertEquals('0', $db->execute('SHOW TABLES'));
}
/**
* @expectedException Exception
* @expectedExceptionMessage No query to execute.
*/
public function testExecuteNoQuery()
{
$db = Database::getInstance();
$db->execute(' ');
}
public function testFetch()
{
$config = Config::getInstance();
$config->data['pickles']['logging'] = true;
$config->data['pickles']['profiler'] = true;
$db = Database::getInstance();
$this->assertEquals([], $db->fetch('SELECT * FROM pickles WHERE id != ?', ['0']));
}
public function testExplainNoInput()
{
$config = Config::getInstance();
$db = Database::getInstance();
$this->assertEquals([], $db->fetch('SELECT * FROM pickles WHERE id != 0'));
}
public function testSlowQuery()
{
$db = Database::getInstance();
$this->assertEquals('0', $db->execute('SHOW DATABASES', null, true));
}
public function testCloseMySQL()
{
$db = Database::getInstance();
$db->open();
$this->assertTrue($db->close());
}
public function testGetInstancePostgreSQL()
{
$config = Config::getInstance();
$config->data['pickles']['datasource'] = 'pgsql';
$config->data['datasources']['pgsql'] = [
'type' => 'pgsql',
'driver' => 'pdo_pgsql',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => 'test',
];
$this->assertInstanceOf('Database', Database::getInstance());
}
/**
* @expectedException PDOException
* @expectedExceptionCode 7
*/
public function testOpenPostgreSQL()
{
// Also throws an exception since I don't have PostgreSQL set up
$config = Config::getInstance();
$db = Database::getInstance();
$db->open();
}
public function testGetInstanceSQLite()
{
$config = Config::getInstance();
$config->data['pickles']['datasource'] = 'sqlite';
$config->data['datasources']['sqlite'] = [
'type' => 'sqlite',
'driver' => 'pdo_sqlite',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => 'test',
];
$this->assertInstanceOf('Database', Database::getInstance());
}
/**
* @expectedException Exception
* @expectedExceptionMessage Datasource driver "pdo_invalid" is invalid
*/
public function testGetInstanceInvalidDriver()
{
$config = Config::getInstance();
$config->data['pickles']['datasource'] = 'invalid';
$config->data['datasources']['invalid'] = [
'type' => 'invalid',
'driver' => 'pdo_invalid',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => 'test',
];
Database::getInstance();
}
}

View file

@ -1,29 +0,0 @@
<?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 [
[date('Y-m-d', $time), '25'],
[date('m/d/Y', $time), '25'],
[date('r', $time), '25'],
['today', '0'],
['400 days ago', '1'],
[true, Date::age('1969-12-31')],
];
}
}

View file

@ -1,132 +0,0 @@
<?php
class DisplayTest extends PHPUnit_Framework_TestCase
{
private $display, $shared_templates;
private $child_html = '<div>child template</div>';
protected function setUp()
{
parent::setUp();
$this->shared_templates = SITE_TEMPLATE_PATH . '__shared/';
if (!file_exists($this->shared_templates))
{
mkdir($this->shared_templates, 0644, true);
}
$_SERVER['REQUEST_URI'] = '/test';
$_REQUEST['request'] = 'test';
$this->display = new Display();
$this->display->module = [
'pickles' => [
'yummy' => 'gherkin',
'delish' => 'kosher dill',
'yucky' => 'bread & butter'
]
];
}
protected function tearDown()
{
unlink(SITE_TEMPLATE_PATH . 'test.phtml');
unlink($this->shared_templates . 'index.phtml');
}
public function testInvalidReturnType()
{
$this->display->return = 'invalid';
$this->assertEquals('Invalid return type.', $this->display->render());
// Gotta do this or the test will be considered "risky"
ob_end_clean();
}
public function testPHPSESSID()
{
$request_uri = $_SERVER['REQUEST_URI'];
$_SERVER['REQUEST_URI'] .= '?PHPSESSID=session_id';
$return = $this->display->render();
$this->assertTrue(in_array('Location: ' . $request_uri, xdebug_get_headers()));
$this->assertEquals('Requested URI contains PHPSESSID, redirecting.', $return);
// Gotta do this or the test will be considered "risky"
ob_end_clean();
}
public function testNoParentTemplate()
{
$child_template = SITE_TEMPLATE_PATH . 'test.phtml';
file_put_contents($child_template, $this->child_html);
$this->display->templates = [$child_template];
$this->assertEquals($this->child_html, $this->display->render());
}
public function testRenderJSON()
{
$this->assertEquals(
'{"pickles":{"yummy":"gherkin","delish":"kosher dill","yucky":"bread & butter"}}',
$this->display->render()
);
}
public function testRenderJSONPrettyPrint()
{
$_REQUEST['pretty'] = 'true';
$pretty_json = <<<JSON
{
"pickles": {
"yummy": "gherkin",
"delish": "kosher dill",
"yucky": "bread & butter"
}
}
JSON;
$this->assertEquals($pretty_json, $this->display->render());
}
public function testRenderXML()
{
$this->display->return = ['template', 'xml'];
$this->assertEquals(
'<yummy>gherkin</yummy><delish>kosher dill</delish><yucky><![CDATA[bread & butter]]></yucky>',
$this->display->render()
);
}
public function testRenderXMLPrettyPrint()
{
$_REQUEST['pretty'] = 'true';
$pretty_xml = <<<XML
<yummy>gherkin</yummy>
<delish>kosher dill</delish>
<yucky><![CDATA[bread & butter]]></yucky>
XML;
$this->display->return = ['template', 'xml'];
$this->assertEquals($pretty_xml, $this->display->render());
}
/*
public function testRenderRSS()
{
$this->fail('Not yet implemented.');
}
public function testRenderRSSPrettyPrint()
{
$this->fail('Not yet implemented.');
}
*/
}

View file

@ -1,75 +0,0 @@
<?php
class DistanceTest extends PHPUnit_Framework_TestCase
{
public function testConvertKilometersToMiles()
{
$this->assertEquals(0.621371, Distance::kilometersToMiles(1));
}
public function testConvertKilometersToMeters()
{
$this->assertEquals(1000, Distance::kilometersToMeters(1));
}
public function testConvertKilometersToYards()
{
$this->assertEquals(1093.61, Distance::kilometersToYards(1));
}
public function testConvertMilesToKilometers()
{
$this->assertEquals(1.60934, Distance::milesToKilometers(1));
}
public function testConvertMilesToMeters()
{
$this->assertEquals(1609.34, Distance::milesToMeters(1));
}
public function testConvertMilesToYards()
{
$this->assertEquals(1760, Distance::milesToYards(1));
}
public function testConvertMetersToKilometers()
{
$this->assertEquals(0.001, Distance::metersToKilometers(1));
}
public function testConvertMetersToMiles()
{
$this->assertEquals(0.000621371, Distance::metersToMiles(1));
}
public function testConvertMetersToYards()
{
$this->assertEquals(1.09361, Distance::metersToYards(1));
}
public function testCalculateDistanceMiles()
{
$this->assertEquals(1003.2646776326, Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94));
}
public function testCalculateDistanceKilometers()
{
$this->assertEquals(1614.5939763012, Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'kilometers'));
}
public function testCalculateDistanceMeters()
{
$this->assertEquals(1614593.9763012, Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'meters'), '', 0.2);
}
public function testCalculateDistanceYards()
{
$this->assertEquals(1765745.8326334, Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'yards'), '', 0.2);
}
public function testNotEnoughUnits()
{
$this->assertFalse(Distance::milesTo(123));
}
}

View file

@ -1,120 +0,0 @@
<?php
class DynamicTest extends PHPUnit_Framework_TestCase
{
private $dynamic;
public static function setUpBeforeClass()
{
// Using actual filesystem because you can't chdir with vfs://
$public_path = '/tmp/pickles-fs/public/';
foreach (['css', 'images', 'js'] as $directory)
{
mkdir($public_path . $directory, 0777, true);
}
touch($public_path . 'images/image.png');
touch($public_path . 'images/invalid');
$css = <<<CSS
body
{
color: #ffcc00;
text-align: center;
}
CSS;
foreach (['css', 'less', 'scss'] as $extension)
{
file_put_contents($public_path . 'css/stylesheet.' . $extension, $css);
}
file_put_contents($public_path . 'css/alternate.css', $css);
$js = <<<JS
function foo()
{
alert('bar');
}
console.log('stuff');
JS;
file_put_contents($public_path . 'js/script.js', $js);
chdir($public_path);
}
public function setUp()
{
$this->dynamic = new Dynamic();
}
public function tearDown()
{
$minified_file = '/tmp/pickles-fs/public/css/stylesheet.min.css';
if (file_exists($minified_file))
{
unlink($minified_file);
}
}
public static function tearDownAfterClass()
{
File::removeDirectory('/tmp/pickles-fs');
}
public function testReference()
{
$this->assertRegExp(
'/^\/images\/image\.\d{10}\.png$/',
$this->dynamic->reference('/images/image.png'
));
}
/**
* @expectedException Exception
* @expectedExceptionMessage Supplied reference does not exist (/images/missing.png)
*/
public function testReferenceMissingFileWithoutFailover()
{
$this->dynamic->reference('/images/missing.png');
}
public function testReferenceMissingFileWithFailover()
{
$this->assertEquals(
'/images/failover.png',
$this->dynamic->reference('/images/missing.png', '/images/failover.png')
);
}
/**
* @expectedException Exception
* @expectedExceptionMessage Filename must have an extension (e.g. /path/to/file.png)
*/
public function testReferenceInvalidFilename()
{
$this->dynamic->reference('/images/invalid');
}
/**
* @expectedException Exception
* @expectedExceptionMessage Reference value must be absolute (e.g. /path/to/file.png)
*/
public function testReferenceNotAbsolute()
{
$this->dynamic->reference('../images/relative.png');
}
public function testReferenceWithQueryString()
{
$this->assertRegExp(
'/^\/images\/image\.\d{10}\.png\?foo=bar$/',
$this->dynamic->reference('/images/image.png?foo=bar'
));
}
}

View file

@ -1,61 +0,0 @@
<?php
class FileTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
// Using actual filesystem because you can't chdir with vfs://
$directory = '/tmp/pickles-fs/filetest/test/test';
if (!file_exists($directory))
{
mkdir($directory, 0777, true);
}
}
public static function tearDownAfterClass()
{
File::removeDirectory('/tmp/pickles-fs');
}
public function testRemoveDirectory()
{
$directory = '/tmp/pickles-fs/filetest/';
touch($directory . 'ing');
touch($directory . 'test/ing');
touch($directory . 'test/test/ing');
File::removeDirectory($directory);
$this->assertFalse(file_exists($directory));
}
public function testMissingTrailingSlash()
{
$directory = SITE_PATH . 'missing';
mkdir($directory, 0777, true);
touch(SITE_PATH . 'missing/slash');
File::removeDirectory($directory);
$this->assertFalse(file_exists($directory));
}
public function testRemoveFileNotDirectory()
{
$directory = SITE_PATH . 'dir';
$file = SITE_PATH . 'dir/file';
mkdir($directory, 0777, true);
touch($file);
File::removeDirectory($file);
$this->assertFalse(file_exists($file));
File::removeDirectory($directory);
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,108 +0,0 @@
<?php
class HTMLTest extends PHPUnit_Framework_TestCase
{
private $html;
public function setUp()
{
$this->html = HTML::getInstance();
}
public function testGetInstance()
{
$this->assertInstanceOf('HTML', $this->html);
}
public function testInput()
{
$this->assertEquals('<input type="text">', $this->html->input());
}
public function testInputDateTimeLocal()
{
$this->assertEquals('<input type="datetime-local">', $this->html->inputDateTimeLocal());
}
public function testInputEmail()
{
$this->assertEquals('<input type="email">', $this->html->inputEmail());
}
public function testInputWithAttributes()
{
$this->assertEquals(
'<input id="id" class="class" value="value" type="text">',
$this->html->input([
'id' => 'id',
'class' => 'class',
'value' => 'value',
])
);
}
public function testInputPasswordWithLabel()
{
$this->assertEquals(
'<label for="password">Enter Password</label><input name="password" type="password">',
$this->html->inputPassword([
'name' => 'password',
'label' => 'Enter Password',
])
);
}
public function testNestedElements()
{
$this->assertEquals(
'<div><p>Nested!</p></div>',
$this->html->div(
$this->html->p('Nested!')
)
);
}
public function testNestedElementsWithAttributes()
{
$this->assertEquals(
'<div class="outer"><p class="inner">Nested!</p></div>',
$this->html->div(
['class' => 'outer'],
$this->html->p(
['class' => 'inner'],
'Nested!'
)
)
);
}
public function testClosingTag()
{
$this->assertEquals('<textarea></textarea>', $this->html->textarea());
}
public function testElement()
{
$this->assertEquals('<div></div>', $this->html->element('div'));
}
public function testReversedParameters()
{
$this->assertEquals(
'<div class="fancy">string</div>',
$this->html->div('string', ['class' => 'fancy'])
);
}
public function testLabelWithInputWithoutName()
{
$this->assertEquals(
'<label>Label</label><input type="text">',
$this->html->input([
'label' => 'Label',
])
);
}
}

View file

@ -1,102 +0,0 @@
<?php
class LogTest extends PHPUnit_Framework_TestCase
{
private $config;
public function setUp()
{
$this->config = Config::getInstance();
$this->config->data['pickles']['logging'] = true;
}
public static function tearDownAfterClass()
{
File::removeDirectory(LOG_PATH);
}
public function testInformation()
{
Log::information('information');
$file = LOG_PATH . date('Y/m/d/') . 'information.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ information$/', $line);
}
public function testWarning()
{
Log::warning('warning');
$file = LOG_PATH . date('Y/m/d/') . 'warning.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ warning$/', $line);
}
public function testError()
{
Log::error('error');
$file = LOG_PATH . date('Y/m/d/') . 'error.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ error$/', $line);
}
public function testSlowQuery()
{
Log::slowQuery('slow query');
$file = LOG_PATH . date('Y/m/d/') . 'slow_query.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ slow query$/', $line);
}
public function testTransaction()
{
Log::transaction('transaction');
$file = LOG_PATH . date('Y/m/d/') . 'transaction.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ transaction$/', $line);
}
public function testPHPError()
{
Log::phperror('php error');
$file = LOG_PATH . date('Y/m/d/') . 'php_error.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^php error$/', $line);
}
public function testQuery()
{
Log::query('query');
$file = LOG_PATH . date('Y/m/d/') . 'query.log';
$data = file($file);
$line = $data[count($data) - 1];
$this->assertRegExp('/^\d{2}:\d{2}:\d{2} .+ query$/', $line);
}
public function testLoggingDisabled()
{
$this->config->data['pickles']['logging'] = false;
$this->assertFalse(Log::error('should return false'));
}
}

View file

@ -1,622 +0,0 @@
<?php
class MockModelWithoutColumns extends Model
{
public $table = 'pickles';
public $columns = false;
}
// InnoDB
class MockModel extends Model
{
public $table = 'pickles';
public $columns = ['created_at' => 'created_at'];
}
// MyISAM
class MyMockModel extends Model
{
public $table = 'mypickles';
}
class ModelTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
// Clears out the Config for ease of testing
Object::$instances = [];
$config = Config::getInstance();
$config->data = [
'pickles' => [
'datasource' => 'mysql',
'cache' => 'memcache',
],
'datasources' => [
'mysql' => [
'type' => 'mysql',
'driver' => 'pdo_mysql',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'test',
'cache' => true,
],
'memcache' => [
'type' => 'memcache',
'hostname' => 'localhost',
'port' => 11211,
'namespace' => '',
],
],
];
for ($i = 0; $i < 5; $i++)
{
$model = new MockModel();
$model->record['field1'] = 'one';
$model->record['field2'] = 'two';
$model->record['field3'] = 'three';
$model->record['field4'] = 'four';
$model->record['field5'] = 'five';
$model->commit();
}
}
/**
* @expectedException Exception
* @expectedExceptionMessage You must set the table variable
*/
public function testNoTable()
{
new Model();
}
public function testWithoutColumns()
{
$model = new MockModelWithoutColumns();
$columns = PHPUnit_Framework_Assert::readAttribute($model, 'columns');
$this->assertFalse($columns['is_deleted']);
}
public function testWithColumns()
{
$model = new MockModel();
$columns = PHPUnit_Framework_Assert::readAttribute($model, 'columns');
$this->assertEquals('is_deleted', $columns['is_deleted']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage You cannot pass in 2 query parameter arrays
*/
public function testDoubleArray()
{
$model = new MockModel(['foo' => 'bar'], ['test' => 'ing']);
}
public function testFetchInt()
{
$model = new MockModel(1);
$this->assertEquals(1, $model->count());
$this->assertEquals(1, $model->record['id']);
}
public function testFetchIntArray()
{
$model = new MockModel([1, 2, 3]);
$this->assertEquals(3, $model->count());
}
/*
@todo Acting wonky, passes tests on just this class, fails on all
public function testFetchConditionsID()
{
$model = new MockModel(['conditions' => ['id' => 1]]);
var_dump($model->record);
$this->assertEquals(1, $model->count());
$this->assertEquals(1, $model->record['id']);
}
*/
public function testFetchCount()
{
$model = new MockModel('count');
$this->assertEquals(5, $model->record['count']);
}
public function testFetchCountConditions()
{
$model = new MockModel('count', ['conditions' => ['id' => [1, 3, 5]]]);
$this->assertEquals(3, $model->record['count']);
}
public function testFetchIndexed()
{
$model = new MockModel('indexed', ['conditions' => ['id' => [2, 4]]]);
$this->assertEquals(2, $model->count());
$this->assertEquals([2, 4], array_keys($model->records));
}
// Also tests against a full cache
public function testFetchList()
{
$model = new MockModel('list', ['conditions' => ['id' => [2, 4]]]);
$this->assertEquals(2, $model->count());
$this->assertEquals([2, 4], array_keys($model->records));
}
public function testFetchCountWithID()
{
$model = new MockModel('count', 3);
$this->assertEquals(1, $model->record['count']);
}
public function testFetchListWithID()
{
$model = new MockModel('list', 2);
$this->assertEquals(1, $model->count());
$this->assertEquals([2 => 'one'], $model->records);
}
public function testFieldValues()
{
$model = new MockModel('all');
$fields = $model->fieldValues('id');
$this->assertEquals('5', count($fields));
foreach ($fields as $value)
{
$this->assertTrue(ctype_digit($value));
}
}
public function testSort()
{
$model = new MockModel();
$this->assertTrue($model->sort('id'));
}
public function testShuffle()
{
$model = new MockModel();
$this->assertTrue($model->shuffle());
}
public function testNextPrev()
{
$model = new MockModel('all');
$model->next();
$this->assertEquals(2, $model->record['id']);
$model->prev();
$this->assertEquals(1, $model->record['id']);
}
public function testLastFirst()
{
$model = new MockModel('all');
$model->last();
$this->assertEquals(5, $model->record['id']);
$model->first();
$this->assertEquals(1, $model->record['id']);
}
public function testEndReset()
{
$model = new MockModel('all');
$model->end();
$this->assertEquals(5, $model->record['id']);
$model->reset();
$this->assertEquals(1, $model->record['id']);
}
public function testWalk()
{
$model = new MockModel('all');
$expected = 0;
while ($model->walk())
{
$expected++;
$this->assertEquals($expected, $model->record['id']);
}
}
public function testInsert()
{
$model = new MockModel();
for ($i = 1; $i <= 5; $i++)
{
$model->record['field' . $i] = String::random();
}
$model->commit();
}
public function testInsertMultiple()
{
$model = new MockModel();
for ($i = 1; $i <= 5; $i++)
{
for ($j = 1; $j <= 5; $j++)
{
$model->record['field' . $j] = String::random();
}
$model->queue();
}
$model->commit();
}
public function testGetFromCache()
{
$model = new MockModel(1);
$this->assertEquals('1', $model->record['id']);
}
public function testGetFromCacheConditionals()
{
$model = new MockModel(['conditions' => ['id' => 1]]);
$this->assertEquals('1', $model->record['id']);
}
public function testCacheKey()
{
$model = new MockModel('indexed', 1, 'cache-key');
$this->assertEquals([1], array_keys($model->records));
}
public function testGenerateQuery()
{
$model = new MockModelWithoutColumns([
'conditions' => [1, 2, 3],
'group' => 'id',
'having' => '1 = 1',
'order' => 'id DESC',
'limit' => 5,
'offset' => 1,
]);
$this->assertEquals('2', $model->record['id']);
}
public function testGenerateConditions()
{
$model = new MockModel();
$conditions = $model->generateConditions([
'id' => [1, 2, 3],
'NOT' => 5,
'OR id !=' => 10,
'OR NOT' => [15, 20, 25],
'id != 30',
'id IS NOT' => null,
'id !=' => false,
'id <' => true,
'id >' => null,
'id BETWEEN' => [35, 40],
]);
$this->assertEquals('id in (?, ?, ?) AND NOT = ? OR id != ? OR NOT in (?, ?, ?) AND id != 30 AND id IS NOT NULL AND id IS NOT FALSE AND id IS TRUE AND id > NULL AND id BETWEEN ? AND ?', $conditions);
}
public function testGenerateConditionsInjectValues()
{
$model = new MockModel();
$conditions = $model->generateConditions([
'id' => [1, 2, 3],
'NOT' => 5,
'OR id !=' => 10,
'OR NOT' => [15, 20, 25],
'id != 30',
'id IS NOT' => null,
'id !=' => false,
'id <' => true,
'id >' => null,
'id BETWEEN' => [35, 40],
], true);
$this->assertEquals('id in (1, 2, 3) AND NOT = 5 OR id != 10 OR NOT in (15, 20, 25) AND id != 30 AND id IS NOT NULL AND id IS NOT FALSE AND id IS TRUE AND id > NULL AND id BETWEEN 35 AND 40', $conditions);
}
public function testGenerateConditionsNoOperatorTrue()
{
$model = new MockModel();
$conditions = $model->generateConditions(['id' => true]);
$this->assertEquals('id IS TRUE', $conditions);
}
public function testGenerateConditionsNoOperatorFalse()
{
$model = new MockModel();
$conditions = $model->generateConditions(['id' => false]);
$this->assertEquals('id IS FALSE', $conditions);
}
public function testGenerateConditionsNoOperatorNull()
{
$model = new MockModel();
$conditions = $model->generateConditions(['id' => null]);
$this->assertEquals('id IS NULL', $conditions);
}
/**
* @expectedException Exception
* @expectedExceptionMessage BETWEEN expects an array with 2 values.
*/
public function testGenerateConditionsBetweenMissingValue()
{
$model = new MockModel();
$conditions = $model->generateConditions(['id BETWEEN' => [1]]);
}
/**
* @expectedException Exception
* @expectedExceptionMessage BETWEEN expects an array.
*/
public function testGenerateConditionsBetweenNotArray()
{
$model = new MockModel();
$conditions = $model->generateConditions(['id BETWEEN' => '1']);
}
public function testCommitSingleRecord()
{
$value = String::random();
$model = new MockModel(1);
$model->record['field1'] = $value;
$model->commit();
$model = new MockModel(1);
$this->assertEquals($value, $model->record['field1']);
}
// Handles filling coverage gaps but isn't a reliable test. Would need to
// test against a table without a UID column so we can see this in action,
// else it just takes a shit because the ID isn't injected back in.
public function testCommitSingleRecordReplace()
{
$value = String::random();
$model = new MockModel(1);
$model->replace = true;
$model->record['field1'] = $value;
$model->commit();
$model = new MockModel(1);
}
public function testCommitInsertPriority()
{
$value = String::random();
$model = new MockModel();
$model->priority = 'low';
$model->record['field1'] = $value;
$id = $model->commit();
$model = new MockModel($id);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitInsertDelayed()
{
$value = String::random();
$model = new MyMockModel();
$model->delayed = true;
$model->record['field1'] = $value;
$model->commit();
$model = new MyMockModel(1);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitInsertIgnore()
{
$value = String::random();
$model = new MockModel();
$model->ignore = true;
$model->record['field1'] = $value;
$id = $model->commit();
$model = new MockModel($id);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitReplacePriority()
{
$value = String::random();
$model = new MockModel();
$model->replace = true;
$model->priority = 'low';
$model->record['field1'] = $value;
$id = $model->commit();
$model = new MockModel($id);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitReplaceDelayed()
{
$value = String::random();
$model = new MyMockModel();
$model->replace = true;
$model->delayed = true;
$model->record['field1'] = $value;
$model->commit();
$model = new MyMockModel(2);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitReplaceIgnore()
{
$value = String::random();
$model = new MockModel();
$model->replace = true;
$model->ignore = true;
$model->record['field1'] = $value;
$id = $model->commit();
$model = new MockModel($id);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitMultipleFields()
{
$value1 = String::random();
$value2 = String::random();
$model = new MockModelWithoutColumns(1);
$model->record['field1'] = $value1;
$model->record['field2'] = $value2;
$model->commit();
$model = new MockModelWithoutColumns(1);
$this->assertEquals($value1, $model->record['field1']);
$this->assertEquals($value2, $model->record['field2']);
}
public function testCommitIncrement()
{
$model = new MockModelWithoutColumns(1);
$model->record['field1'] = 100;;
$model->commit();
$model = new MockModelWithoutColumns(1);
$model->record['field1'] = '++';
$model->commit();
$model = new MockModelWithoutColumns(1);
$this->assertEquals(101, $model->record['field1']);
}
public function testCommitUpdatedID()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$value = String::random();
$model = new MockModel(1);
$model->record['field1'] = $value;
$model->commit();
$model = new MockModel(1);
$this->assertEquals($value, $model->record['field1']);
$this->assertEquals(1, $model->record['updated_id']);
}
public function testCommitCreatedID()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$value = String::random();
$model = new MockModel();
$model->record['field1'] = $value;
$id = $model->commit();
$model = new MockModel($id);
$this->assertEquals(1, $model->record['created_id']);
}
// Doesn't test against actual PostgreSQL instance, just for valid syntax
public function testCommitInsertPostgreSQL()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$value = String::random();
$model = new MockModel();
$model->mysql = false;
$model->postgresql = true;
$model->record['field1'] = $value;
try
{
$model->commit();
}
catch (Exception $e)
{
}
$this->assertRegExp('/RETURNING id/', $model->db->results->queryString);
}
// Doesn't test against actual PostgreSQL instance, just for valid syntax
public function testCommitUpdatePostgreSQL()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$value = String::random();
$model = new MockModel(1);
$model->mysql = false;
$model->postgresql = true;
$model->record['field1'] = $value;
try
{
$model->commit();
}
catch (Exception $e)
{
}
$model = new MockModel(1);
$this->assertEquals($value, $model->record['field1']);
}
public function testCommitNothing()
{
$model = new MockModel();
$this->assertFalse($model->commit());
}
public function testDeleteLogical()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$model = new MockModel(1);
$model->delete();
$model = new MockModel(1);
$this->assertEquals([], $model->record);
}
public function testDeleteActual()
{
$model = new MockModelWithoutColumns(2);
$model->delete();
$model = new MockModelWithoutColumns(2);
$this->assertEquals(0, $model->count());
}
public function testDeleteNothing()
{
$model = new MockModelWithoutColumns(100);
$this->assertFalse($model->delete());
}
public function testLoadParametersWithString()
{
$model = new MockModel();
$this->assertFalse($model->loadParameters(''));
}
public function testMultipleQueueInsert()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$model = new MockModel('count');
$count = $model->record['count'];
$model = new MockModel();
for ($i = 0; $i < 5; $i++)
{
$model->record['field1'] = String::random();
$model->record['updated_id'] = 1;
$model->queue();
}
$model->commit();
$model = new MockModel('count');
$this->assertEquals($count + 5, $model->record['count']);
}
public function testMultipleQueueUpdate()
{
$_SESSION['__pickles']['security']['user_id'] = 1;
$model = new MockModel();
for ($i = 3; $i <= 5; $i++)
{
$model->record['id'] = $i;
$model->record['field1'] = String::random();
$model->record['updated_id'] = 1;
$model->queue();
}
$model->commit();
}
}

View file

@ -1,69 +0,0 @@
<?php
$_POST['field2'] = 'short';
$_GET['field2'] = 'short';
$_REQUEST['field2'] = 'short';
class MockParentModule extends Module
{
public $validate = [
'field1',
'field2' => [
'length:<:10' => 'Too short',
'length:>:50' => 'Too long',
],
];
}
class MockChildModule extends MockParentModule
{
public $method = ['POST', 'GET'];
}
class ModuleTest extends PHPUnit_Framework_TestCase
{
public function testAutoRun()
{
$this->assertInstanceOf('Module', new Module(true));
}
public function testAutoRunParentError()
{
$this->expectOutputString('');
$model = new MockChildModule(true);
}
public function testSetGetReturn()
{
$module = new Module();
$module->foo = 'bar';
$this->assertEquals('bar', $module->foo);
}
public function testGetMissing()
{
$module = new Module();
$this->assertFalse($module->missing);
}
public function testValidateGet()
{
$module = new MockParentModule();
$module->method = 'GET';
$this->assertEquals(['The field1 field is required.', 'Too long'], $module->__validate());
}
public function testValidatePost()
{
$module = new MockParentModule();
$this->assertEquals(['The field1 field is required.', 'Too long'], $module->__validate());
}
public function testValidateRequest()
{
$module = new MockParentModule();
$module->method = null;
$this->assertEquals(['The field1 field is required.', 'Too long'], $module->__validate());
}
}

View file

@ -1,49 +0,0 @@
<?php
class NumberTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerOrginalIndicatorNoSuper
*/
public function testOrdinalIndicatorNoSuper($a, $b)
{
$this->assertEquals($b, Number::ordinalIndicator($a));
}
public function providerOrginalIndicatorNoSuper()
{
return [
[1, '1st'],
[2, '2nd'],
[3, '3rd'],
[4, '4th'],
[51, '51st'],
[52, '52nd'],
[53, '53rd'],
[54, '54th'],
];
}
/**
* @dataProvider providerOrginalIndicatorSuper
*/
public function testOrdinalIndicatorSuper($a, $b)
{
$this->assertEquals($b, Number::ordinalIndicator($a, true));
}
public function providerOrginalIndicatorSuper()
{
return [
[1, '1<sup>st</sup>'],
[2, '2<sup>nd</sup>'],
[3, '3<sup>rd</sup>'],
[4, '4<sup>th</sup>'],
[51, '51<sup>st</sup>'],
[52, '52<sup>nd</sup>'],
[53, '53<sup>rd</sup>'],
[54, '54<sup>th</sup>'],
];
}
}

View file

@ -1,24 +0,0 @@
<?php
class ObjectTest extends PHPUnit_Framework_TestCase
{
public function testConstructorWithoutObjects()
{
$object = new Object();
$this->assertInstanceOf('Config', PHPUnit_Framework_Assert::readAttribute($object, 'config'));
}
public function testConstructorWithObjects()
{
$object = new Object('cache');
$this->assertInstanceOf('Cache', PHPUnit_Framework_Assert::readAttribute($object, 'cache'));
}
public function testGetInstanceWithoutClass()
{
$this->assertFalse(Object::getInstance());
}
}

View file

@ -1,81 +0,0 @@
<?php
class ProfilerTest extends PHPUnit_Framework_TestCase
{
public function testReport()
{
$this->expectOutputRegex('//');
Profiler::report();
}
public function testDisabledType()
{
$config = Config::getInstance();
$config->data['pickles']['profiler'] = false;
$this->assertFalse(Profiler::enabled('timers'));
}
public function testTimerDisabled()
{
$this->assertFalse(Profiler::timer('disabled'));
}
public function testReportNothing()
{
$this->expectOutputRegex('/There is nothing to profile/');
Profiler::report();
}
public function testEnabled()
{
$config = Config::getInstance();
$config->data['pickles']['profiler'] = true;
$this->assertTrue(Profiler::enabled());
}
public function testEnabledType()
{
$config = Config::getInstance();
$config->data['pickles']['profiler'] = 'timers';
$this->assertTrue(Profiler::enabled('timers'));
}
public function testLogAndTimer()
{
Profiler::log('timer', 'timer-one');
Profiler::log(['foo' => 'bar']);
Profiler::log(new Object);
Profiler::log('string');
Profiler::log(3.14, 'method', true);
Profiler::log('timer', 'timer-one');
}
public function testLogQuery()
{
$explain = [
[
'key' => '',
'possible_keys' => '',
'type' => '',
'rows' => '',
'Extra' => '',
],
];
Profiler::logQuery('SELECT * FROM table;');
Profiler::logQuery('SELECT * FROM table WHERE column = ?;', ['foo']);
Profiler::logQuery('SELECT * FROM table;', false, $explain);
}
public function testTimer()
{
Profiler::timer('timer-two');
Profiler::timer('timer-two');
}
}

View file

@ -1,153 +0,0 @@
<?php
class MockUserModel extends Model
{
public $table = 'users';
}
class SecurityTest extends PHPUnit_Framework_TestCase
{
public function testGenerateHashWithDefaultSalts()
{
$this->assertEquals(
'4940e793006aa897db22751bba80dff4cb6a3e08',
Security::generateHash('source')
);
}
public function testGenerateHashWithCustomSalts()
{
$config = Config::getInstance();
$config->data['security']['salt'] = 'salt';
$this->assertEquals(
'4eac88c934c33cfa9a80c0b2eb322f23ac3b13c5',
Security::generateHash('source')
);
}
public function testGenerateSHA256Hash()
{
$this->assertEquals(
'3d04f805aff4838ecaf98c7260a813fffd2b7a8a7f957add8018908a1bbdad04',
Security::generateSHA256Hash('source', 'salt')
);
}
public function testLogin()
{
$this->assertTrue(Security::login(1, 10, 'USER'));
$this->assertTrue(isset($_SESSION['__pickles']['security']));
}
public function testLoginNoSession()
{
session_destroy();
$this->assertFalse(Security::login(1, 10, 'USER'));
}
public function testLogout()
{
session_start();
Security::login(1, 10, 'USER');
$this->assertTrue(Security::logout());
$this->assertFalse(isset($_SESSION['__pickles']['security']));
}
public function testIsLevel()
{
Security::login(1, 10, 'USER');
$this->assertTrue(Security::isLevel(SECURITY_LEVEL_USER));
}
public function testIsLevelArray()
{
Security::login(1, 10, 'USER');
$this->assertTrue(Security::isLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]));
}
public function testHasLevel()
{
Security::login(1, 10, 'USER');
$this->assertTrue(Security::hasLevel(SECURITY_LEVEL_USER));
}
public function testHasLevelArray()
{
Security::login(1, 10, 'USER');
$this->assertTrue(Security::hasLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]));
}
public function testBetweenLevel()
{
Security::login(1, 10, 'USER');
$this->assertTrue(Security::betweenLevel(SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN));
}
public function testTokenMismatch()
{
Security::login(1, 10, 'USER');
$_SESSION['__pickles']['security']['token'] = 'foo';
$_COOKIE['pickles_security_token'] = 'bar';
$this->assertFalse(Security::isLevel(SECURITY_LEVEL_USER));
}
public function testIsLevelDB()
{
$config = Config::getInstance();
$config->data = [
'pickles' => [
'datasource' => 'mysql',
'cache' => 'memcache',
],
'datasources' => [
'mysql' => [
'type' => 'mysql',
'driver' => 'pdo_mysql',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => 'test',
'cache' => true,
],
'memcache' => [
'type' => 'memcache',
'hostname' => 'localhost',
'port' => 11211,
'namespace' => '',
],
],
'security' => ['model' => 'MockUserModel'],
];
$model = new MockUserModel();
$model->record['username'] = 'pickles';
$model->commit();
setUpConfig([
]);
new Config();
Security::login(1, 10, 'USER');
unset(
$_SESSION['__pickles']['security']['token'],
$_COOKIE['pickles_security_token'],
$_SESSION['__pickles']['security']['level']
);
$this->assertFalse(Security::isLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]));
}
}

View file

@ -1,98 +0,0 @@
<?php
class SessionTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
if (session_id())
{
session_destroy();
}
$_SERVER['HTTP_USER_AGENT'] = 'yes';
$_SERVER['REQUEST_METHOD'] = 'GET';
}
public function testFiles()
{
$config = Config::getInstance();
$config->data['pickles']['sessions'] = 'files';
new Session();
$_SESSION['test'] = 'files';
$this->assertEquals('files', $_SESSION['test']);
}
public function testMemcache()
{
$config = Config::getInstance();
$config->data['pickles']['sessions'] = 'memcache';
$config->data['datasources']['memcache'] = [
'type' => 'memcache',
'hostname' => 'localhost',
'port' => '11211',
];
new Session();
$_SESSION['test'] = 'memcache';
$this->assertEquals('memcache', $_SESSION['test']);
}
public function testMemcached()
{
$config = Config::getInstance();
$config->data['pickles']['sessions'] = 'memcached';
$config->data['datasources']['memcached'] = [
'type' => 'memcached',
'hostname' => 'localhost',
'port' => '11211',
];
new Session();
$_SESSION['test'] = 'memcached';
$this->assertEquals('memcached', $_SESSION['test']);
}
public function testRedis()
{
$config = Config::getInstance();
$config->data['pickles']['sessions'] = 'redis';
$config->data['datasources']['redis'] = [
'type' => 'redis',
'hostname' => 'localhost',
'port' => '6379',
'database' => '1',
'prefix' => 'p:',
];
new Session();
$_SESSION['test'] = 'redis';
$this->assertEquals('redis', $_SESSION['test']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage You must provide both the hostname and port for the datasource.
*/
public function testMissingHostname()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$config = Config::getInstance();
$config->data['pickles']['sessions'] = 'redis';
$config->data['datasources']['redis'] = [
'type' => 'redis',
'port' => '6379',
];
new Session();
$_SESSION['test'] = 'redis';
$this->assertEquals('redis', $_SESSION['test']);
}
}

View file

@ -1,61 +0,0 @@
<?php
class SortTest extends PHPUnit_Framework_TestCase
{
public function testByNameASC()
{
$shuffled = [
['name' => 'epsilon'],
['name' => 'gamma'],
['name' => 'alpha'],
['name' => 'delta'],
['name' => 'beta'],
];
$sorted = [
['name' => 'alpha'],
['name' => 'beta'],
['name' => 'delta'],
['name' => 'epsilon'],
['name' => 'gamma'],
];
Sort::by('name', $shuffled);
$this->assertEquals($sorted, $shuffled);
}
public function testByNameDESC()
{
$shuffled = [
['name' => 'epsilon'],
['name' => 'gamma'],
['name' => 'alpha'],
['name' => 'delta'],
['name' => 'beta'],
];
$sorted = [
['name' => 'gamma'],
['name' => 'epsilon'],
['name' => 'delta'],
['name' => 'beta'],
['name' => 'alpha'],
];
Sort::by('name', $shuffled, Sort::DESC);
$this->assertEquals($sorted, $shuffled);
}
public function testMissingField()
{
$shuffled = [['foo' => 'bar', 'bar' => 'foo']];
$sorted = [['foo' => 'bar', 'bar' => 'foo']];
Sort::by('name', $shuffled);
$this->assertEquals($sorted, $shuffled);
}
}

View file

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

View file

@ -1,199 +0,0 @@
<?php
class ValidateTest extends PHPUnit_Framework_TestCase
{
public function testFilterBoolean()
{
$this->assertTrue(Validate::isValid(true, ['filter:boolean' => 'error']));
}
public function testFilterBooleanError()
{
$this->assertEquals(['error'], Validate::isValid(false, ['filter:boolean' => 'error']));
}
public function testFilterEmail()
{
$this->assertTrue(Validate::isValid('foo@bar.com', ['filter:email' => 'error']));
}
public function testFilterEmailError()
{
$this->assertEquals(['error'], Validate::isValid('invalid', ['filter:email' => 'error']));
}
public function testFilterFloat()
{
$this->assertTrue(Validate::isValid(2.231981, ['filter:float' => 'error']));
}
public function testFilterFloatError()
{
$this->assertEquals(['error'], Validate::isValid('invalid', ['filter:float' => 'error']));
}
public function testFilterInt()
{
$this->assertTrue(Validate::isValid(2231981, ['filter:int' => 'error']));
}
public function testFilterIntError()
{
$this->assertEquals(['error'], Validate::isValid('invalid', ['filter:int' => 'error']));
}
public function testFilterIP()
{
$this->assertTrue(Validate::isValid('2.23.19.81', ['filter:ip' => 'error']));
}
public function testFilterIPError()
{
$this->assertEquals(['error'], Validate::isValid('invalid', ['filter:ip' => 'error']));
}
public function testFilterURL()
{
$this->assertTrue(Validate::isValid('http://foo.com/bar?stuff', ['filter:url' => 'error']));
}
public function testFilterURLError()
{
$this->assertEquals(['error'], Validate::isValid('invalid', ['filter:url' => 'error']));
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid validation rule, expected: "validate:boolean|email|float|int|ip|url".
*/
public function testFilterVarInvalidRule()
{
Validate::isValid('value', ['filter' => 'foo']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid filter, expecting boolean, email, float, int, ip or url.
*/
public function testFilterVarInvalidFilter()
{
Validate::isValid('value', ['filter:foo' => 'bar']);
}
public function testLengthLessThan()
{
$this->assertTrue(Validate::isValid('value', ['length:<:10' => 'error']));
}
public function testLengthLessThanError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:<:1' => 'error']));
}
public function testLengthLessThanOrEqual()
{
$this->assertTrue(Validate::isValid('value', ['length:<=:10' => 'error']));
}
public function testLengthLessThanOrEqualError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:<=:1' => 'error']));
}
public function testLengthEqual()
{
$this->assertTrue(Validate::isValid('value', ['length:==:5' => 'error']));
}
public function testLengthEqualError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:==:1' => 'error']));
}
public function testLengthNotEqual()
{
$this->assertTrue(Validate::isValid('value', ['length:!=:1' => 'error']));
}
public function testLengthNotEqualError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:!=:5' => 'error']));
}
public function testLengthGreaterThanOrEqual()
{
$this->assertTrue(Validate::isValid('value', ['length:>=:1' => 'error']));
}
public function testLengthGreaterThanOrEqualError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:>=:10' => 'error']));
}
public function testLengthGreaterThan()
{
$this->assertTrue(Validate::isValid('value', ['length:>:1' => 'error']));
}
public function testLengthGreaterThanError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['length:>:10' => 'error']));
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid validation rule, expected: "length:<|<=|==|!=|>=|>:integer".
*/
public function testLengthInvalidRule()
{
Validate::isValid('value', ['length:16' => 'bar']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid length value, expecting an integer.
*/
public function testLengthInvalidLength()
{
Validate::isValid('value', ['length:==:foo' => 'bar']);
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid operator, expecting <, <=, ==, !=, >= or >.
*/
public function testLengthInvalidOperator()
{
Validate::isValid('value', ['length:&&:10' => 'foo']);
}
public function testRegexIs()
{
$this->assertTrue(Validate::isValid('value', ['regex:is:/^va7ue$/' => 'error']));
}
public function testRegexIsError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['regex:is:/^value$/' => 'error']));
}
public function testRegexNot()
{
$this->assertTrue(Validate::isValid('value', ['regex:not:/^value$/' => 'error']));
}
public function testRegexNotError()
{
$this->assertEquals(['error'], Validate::isValid('value', ['regex:not:/^va7ue$/' => 'error']));
}
/**
* @expectedException Exception
* @expectedExceptionMessage Invalid validation rule, expected: "regex:is|not:string".
*/
public function testRegexInvalidRule()
{
Validate::isValid('value', ['regex:/foo/' => 'bar']);
}
}

View file

@ -53,3 +53,5 @@ CREATE TABLE `users` (
`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());

15
tests/travis.sh Executable file
View file

@ -0,0 +1,15 @@
#!/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