Browse Source

Removed the included source code of Fat-Free Framework and updated `setup.sh`

Dennis Meckel 9 years ago
parent
commit
7b9c56257c

+ 11 - 4
frameworks/PHP/fat-free/setup.sh

@@ -3,10 +3,17 @@
 fw_depends php nginx
 
 sed -i 's|localhost|'"${DBHOST}"'|g' index.php
-sed -i 's|.*/FrameworkBenchmarks/fat-free|'"${TROOT}"'|g' deploy/fat-free
-sed -i 's|Directory .*/FrameworkBenchmarks/fat-free|Directory '"${TROOT}"'|g' deploy/fat-free
 sed -i 's|root .*/FrameworkBenchmarks/fat-free|root '"${TROOT}"'|g' deploy/nginx.conf
 sed -i 's|/usr/local/nginx/|'"${IROOT}"'/nginx/|g' deploy/nginx.conf
 
-php-fpm --fpm-config $FWROOT/config/php-fpm.conf -g $TROOT/deploy/php-fpm.pid
-nginx -c $TROOT/deploy/nginx.conf
+F3DIR="$TROOT/src"
+
+[[ ! -e "$F3DIR" ]] || rm -r "$F3DIR"
+
+git clone "https://github.com/bcosca/fatfree-core.git" "$F3DIR"
+pushd "$F3DIR" > /dev/null
+git checkout -q "b284f0c482e858f0162cab529925e7de14d90746" # v3.5.0
+popd > /dev/null
+
+php-fpm --fpm-config "$FWROOT/config/php-fpm.conf" -g "$TROOT/deploy/php-fpm.pid"
+nginx -c "$TROOT/deploy/nginx.conf"

+ 0 - 577
frameworks/PHP/fat-free/src/CHANGELOG

@@ -1,577 +0,0 @@
-CHANGELOG
-
-3.5.0 (2 June 2015)
-*	NEW: until() method for long polling
-*	NEW: abort() to disconnect HTTP client (and continue execution)
-*	NEW: SQL Mapper->required() returns TRUE if field is not nullable
-*	NEW: PREMAP variable for allowing prefixes to handlers named after HTTP verbs
-*	NEW: [configs] section to allow config includes
-*	NEW: Test->passed() returns TRUE if no test failed
-*	NEW: SQL mapper changed() function
-*	NEW: fatfree-core composer support
-*	NEW: constants() method to expose constants
-*	NEW: Preview->filter() for configurable token filters
-*	NEW: CORS variable for Cross-Origin Resource Sharing support, #731
-*	Change in behavior: Switch to htmlspecialchars for escaping
-*	Change in behavior: No movement in cursor position after erase(), #797
-*	Change in behavior: ERROR.trace is a multiline string now
-*	Change in behavior: Strict token recognition in <include> href attribute
-*	Router fix: loose method search
-*	Better route precedence order, #12
-*	Preserve contents of ROUTES, #723
-*	Alias: allow array of parameters
-*	Improvements on reroute method
-*	Fix for custom Jig session files
-*	Audit: better mobile detection
-*	Audit: add argument to test string as browser agent
-*	DB mappers: abort insert/update/erase from hooks, #684
-*	DB mappers: Allow array inputs in copyfrom()
-*	Cache,SQL,Jig,Mongo Session: custom callback for suspect sessions
-*	Fix for unexpected HIVE values when defining an empty HIVE array
-*	SQL mapper: check for results from CALL and EXEC queries, #771
-*	SQL mapper: consider SQL schema prefix, #820
-*	SQL mapper: write to log before execution to
-	enable tracking of PDOStatement error
-*	Add SQL Mapper->table() to return table name
-*	Allow override of the schema in SQL Mapper->schema()
-*	Improvement: Keep JIG table as reference, #758
-*	Expand regex to include whitespaces in SQL DB dsn, #817
-*	View: Removed reserved variables $fw and $implicit
-*	Add missing newlines after template expansion
-*	Web->receive: fix for complex field names, #806
-*	Web: Improvements in socket engine
-*	Web: customizable user_agent for all engines, #822
-*	SMTP: Provision for Content-ID in attachments
-*	Image + minify: allow absolute paths
-*	Promote framework error to E_USER_ERROR
-*	Geo->weather switch to OpenWeather
-*	Expose mask() and grab() methods for routing
-*	Expose trace() method to expose the debug backtrace
-*	Implement recursion strategy using IteratorAggregate, #714
-*	Exempt whitespace between % and succeeding operator from being minified, #773
-*	Optimized error detection and ONERROR handler, fatfree-core#18
-*	Tweak error log output
-*	Optimized If-Modified-Since cache header usage
-*	Improved APCu compatibility, #724
-*	Bug fix: Web::send fails on filename with spaces, #810
-*	Bug fix: overwrite limit in findone()
-*	Bug fix: locale-specific edge cases affecting SQL schema, #772
-*	Bug fix: Newline stripping in config()
-*	Bug fix: bracket delimited identifier for sybase and dblib driver
-*	Bug fix: Mongo mapper collection->count driver compatibility
-*	Bug fix: SQL Mapper->set() forces adhoc value if already defined
-*	Bug fix: Mapper ignores HAVING clause
-*	Bug fix: Constructor invocation in call()
-*	Bug fix: Wrong element returned by ajax/sync request
-*	Bug fix: handling of non-consecutive compound key members
-*	Bug fix: Virtual fields not retrieved when group option is present, #757
-*	Bug fix: group option generates incorrect SQL query, #757
-*	Bug fix: ONERROR does not receive PARAMS on fatal error
-
-3.4.0 (1 January 2015)
-*	NEW: [redirects] section
-*	NEW: Custom config sections
-*	NEW: User-defined AUTOLOAD function
-*	NEW: ONREROUTE variable
-*	NEW: Provision for in-memory Jig database (#727)
-*	Return run() result (#687)
-*	Pass result of run() to mock() (#687)
-*	Add port suffix to REALM variable
-*	New attribute in <include> tag to extend hive
-*	Adjust unit tests and clean up templates
-*	Expose header-related methods
-*	Web->request: allow content array
-*	Preserve contents of ROUTES (#723)
-*	Smart detection of PHP functions in template expressions
-*	Add afterrender() hook to View class
-*	Implement ArrayAccess and magic properties on hive
-*	Improvement on mocking of superglobals and request body
-*	Fix table creation for pgsql handled sessions
-*	Add QUERY to hive
-*	Exempt E_NOTICE from default error_reporting()
-*	Add method to build alias routes from template, fixes #693
-*	Fix dangerous caching of cookie values
-*	Fix multiple encoding in nested templates
-*	Fix node attribute parsing for empty/zero values
-*	Apply URL encoding on BASE to emulate v2 behavior (#123)
-*	Improve Base->map performance (#595)
-*	Add simple backtrace for fatal errors
-*	Count Cursor->load() results (#581)
-*	Add form field name to Web->receive() callback arguments
-*	Fix missing newlines after template expansion
-*	Fix overwrite of ENCODING variable
-*	limit & offset workaround for SQL Server, fixes #671
-*	SQL Mapper->find: GROUP BY SQL compliant statement
-*	Bug fix: Missing abstract method fields()
-*	Bug fix: Auto escaping does not work with mapper objects (#710)
-*	Bug fix: 'with' attribute in <include> tag raise error when no token
-	inside
-*	View rendering: optional Content-Type header
-*	Bug fix: Undefined variable: cache (#705)
-*	Bug fix: Routing does not work if project base path includes valid
-	special URI character (#704)
-*	Bug fix: Template hash collision (#702)
-*	Bug fix: Property visibility is incorrect (#697)
-*	Bug fix: Missing Allow header on HTTP 405 response
-*	Bug fix: Double quotes in lexicon files (#681)
-*	Bug fix: Space should not be mandatory in ICU pluralization format string
-*	Bug fix: Incorrect log entry when SQL query contains a question mark
-*	Bug fix: Error stack trace
-*	Bug fix: Cookie expiration (#665)
-*	Bug fix: OR operator (||) parsed incorrectly
-*	Bug fix: Routing treatment of * wildcard character
-*	Bug fix:  Mapper copyfrom() method doesn't allow class/object callbacks
-	(#590)
-*	Bug fix: exists() creates elements/properties (#591)
-*	Bug fix: Wildcard in routing pattern consumes entire query string (#592)
-*	Bug fix: Workaround bug in latest MongoDB driver
-*	Bug fix: Default error handler silently fails for AJAX request with
-	DEBUG>0 (#599)
-*	Bug fix: Mocked BODY overwritten (#601)
-*	Bug fix: Undefined pkey (#607)
-
-3.3.0 (8 August 2014)
-*	NEW: Attribute in <include> tag to extend hive
-*	NEW: Image overlay with transparency and alignment control
-*	NEW: Allow redirection of specified route patterns to a URL
-*	Bug fix: Missing AND operator in SQL Server schema query (Issue #576)
-*	Count Cursor->load() results (Feature request #581)
-*	Mapper copyfrom() method doesn't allow class/object callbacks (Issue #590)
-*	Bug fix: exists() creates elements/properties (Issue #591)
-*	Bug fix: Wildcard in routing pattern consumes entire query string
-	(Issue #592)
-*	Tweak Base->map performance (Issue #595)
-*	Bug fix: Default error handler silently fails for AJAX request with
-	DEBUG>0 (Issue #599)
-*	Bug fix: Mocked BODY overwritten (Issue #601)
-*	Bug fix: Undefined pkey (Issue #607)
-*	Bug fix: beforeupdate() position (Issue #633)
-*	Bug fix: exists() return value for cached keys
-*	Bug fix: Missing error code in UNLOAD handler
-*	Bug fix: OR operator (||) parsed incorrectly
-*	Add input name parameter to custom slug function
-*	Apply URL encoding on BASE to emulate v2 behavior (Issue #123)
-*	Reduce mapper update() iterations
-*	Bug fix: Routing treatment of * wildcard character
-*	SQL Mapper->find: GROUP BY SQL compliant statement
-*	Work around bug in latest MongoDB driver
-*	Work around probable race condition and optimize cache access
-*	View rendering: Optional Content-Type header
-*	Fix missing newlines after template expansion
-*	Add form field name to Web->receive() callback arguments
-*	Quick reference: add RAW variable
-
-3.2.2 (19 March 2014)
-*	NEW: Locales set automatically (Feature request #522)
-*	NEW: Mapper dbtype()
-*	NEW: before- and after- triggers for all mappers
-*	NEW: Decode HTML5 entities if PHP>5.3 detected (Feature request #552)
-*	NEW: Send credentials only if AUTH is present in the SMTP extension
-	response (Feature request #545)
-*	NEW: BITMASK variable to allow ENT_COMPAT override
-*	NEW: Redis support for caching
-*	Enable SMTP feature detection
-*	Enable extended ICU custom date format (Feature request #555)
-*	Enable custom time ICU format
-*	Add option to turn off session table creation (Feature request #557)
-*	Enhanced template token rendering and custom filters (Feature request
-	#550)
-*	Avert multiple loads in DB-managed sessions (Feature request #558)
-*	Add EXEC to associative fetch
-*	Bug fix: Building template tokens breaks on inline OR condition (Issue
-	#573)
-*	Bug fix: SMTP->send does not use the $log parameter (Issue #571)
-*	Bug fix: Allow setting sqlsrv primary keys on insert (Issue #570)
-*	Bug fix: Generated query for obtaining table schema in sqlsrv incorrect
-	(Bug #565)
-*	Bug fix: SQL mapper flag set even when value has not changed (Bug #562)
-*	Bug fix: Add XFRAME config option (Feature request #546)
-*	Bug fix: Incorrect parsing of comments (Issue #541)
-*	Bug fix: Multiple Set-Cookie headers (Issue #533)
-*	Bug fix: Mapper is dry after save()
-*	Bug fix: Prevent infinite loop when error handler is triggered
-	(Issue #361)
-*	Bug fix: Mapper tweaks not passing primary keys as arguments
-*	Bug fix: Zero indexes in dot-notated arrays fail to compile
-*	Bug fix: Prevent GROUP clause double-escaping
-*	Bug fix: Regression of zlib compression bug
-*	Bug fix: Method copyto() does not include ad hoc fields
-*	Check existence of OpenID mode (Issue #529)
-*	Generate a 404 when a tokenized class doesn't exist
-*	Fix SQLite quotes (Issue #521)
-*	Bug fix: BASE is incorrect on Windows
-
-3.2.1 (7 January 2014)
-*	NEW: EMOJI variable, UTF->translate(), UTF->emojify(), and UTF->strrev()
-*	Allow empty strings in config()
-*	Add support for turning off php://input buffering via RAW
-	(FALSE by default)
-*	Add Cursor->load() and Cursor->find() TTL support
-*	Support Web->receive() large file downloads via PUT
-*	ONERROR safety check
-*	Fix session CSRF cookie detection
-*	Framework object now passed to route handler contructors
-*	Allow override of DIACRITICS
-*	Various code optimizations
-*	Support log disabling (Issue #483)
-*	Implicit mapper load() on authentication
-*	Declare abstract methods for Cursor derivatives
-*	Support single-quoted HTML/XML attributes (Feature request #503)
-*	Relax property visibility of mappers and derivatives
-*	Deprecated: {{~ ~}} instructions and {{* *}} comments; Use {~ ~} and
-	{* *} instead
-*	Minor fix: Audit->ipv4() return value
-*	Bug fix: Backslashes in BASE not converted on Windows
-*	Bug fix: UTF->substr() with negative offset and specified length
-*	Bug fix: Replace named URL tokens on render()
-*	Bug fix: BASE is not empty when run from document root
-*	Bug fix: stringify() recursion
-
-3.2.0 (18 December 2013)
-*	NEW: Automatic CSRF protection (with IP and User-Agent checks) for
-	sessions mapped to SQL-, Jig-, Mongo- and Cache-based backends
-*	NEW: Named routes
-*	NEW: PATH variable; returns the URL relative to BASE
-*	NEW: Image->captcha() color parameters
-*	NEW: Ability to access MongoCuror thru the cursor() method
-*	NEW: Mapper->fields() method returns array of field names
-*	NEW: Mapper onload(), oninsert(), onupdate(), and onerase() event
-	listeners/triggers
-*	NEW: Preview class (a lightweight template engine)
-*	NEW: rel() method derives path from URL relative to BASE; useful for
-	rerouting
-*	NEW: PREFIX variable for prepending a string to a dictionary term;
-	Enable support for prefixed dictionary arrays and .ini files (Feature
-	request #440)
-*	NEW: Google static map plugin
-*	NEW: devoid() method
-*	Introduce clean(); similar to scrub(), except that arg is passed by
-	value
-*	Use $ttl for cookie expiration (Issue #457)
-*	Fix needs_rehash() cost comparison
-*	Add pass-by-reference argument to exists() so if method returns TRUE,
-	a subsequent get() is unnecessary
-*	Improve MySQL support
-*	Move esc(), raw(), and dupe() to View class where they more
-	appropriately belong
-*	Allow user-defined fields in SQL mapper constructor (Feature request
-	#450)
-*	Re-implement the pre-3.0 template resolve() feature
-*	Remove redundant instances of session_commit()
-*	Add support for input filtering in Mapper->copyfrom()
-*	Prevent intrusive behavior of Mapper->copyfrom()
-*	Support multiple SQL primary keys
-*	Support custom tag attributes/inline tokens defined at runtime
-	(Feature request #438)
-*	Broader support for HTTP basic auth
-*	Prohibit Jig _id clear()
-*	Add support for detailed stringify() output
-*	Add base directory to UI path as fallback
-*	Support Test->expect() chaining
-*	Support __tostring() in stringify()
-*	Trigger error on invalid CAPTCHA length (Issue #458)
-*	Bug fix: exists() pass-by-reference argument returns incorrect value
-*	Bug fix: DB Exec does not return affected row if query contains a
-	sub-SELECT (Issue #437)
-*	Improve seed generator and add code for detecting of acceptable
-	limits in Image->captcha() (Feature request #460)
-*	Add decimal format ICU extension
-*	Bug fix: 404-reported URI contains HTTP query
-*	Bug fix: Data type detection in DB->schema()
-*	Bug fix: TZ initialization
-*	Bug fix: paginate() passes incorrect argument to count()
-*	Bug fix: Incorrect query when reloading after insert()
-*	Bug fix: SQL preg_match error in pdo_type matching (Issue #447)
-*	Bug fix: Missing merge() function (Issue #444)
-*	Bug fix: BASE misdefined in command line mode
-*	Bug fix: Stringifying hive may run infinite (Issue #436)
-*	Bug fix: Incomplete stringify() when DEBUG<3 (Issue #432)
-*	Bug fix: Redirection of basic auth (Issue #430)
-*	Bug fix: Filter only PHP code (including short tags) in templates
-*	Bug fix: Markdown paragraph parser does not convert PHP code blocks
-	properly
-*	Bug fix: identicon() colors on same keys are randomized
-*	Bug fix: quotekey() fails on aliased keys
-*	Bug fix: Missing _id in Jig->find() return value
-*	Bug fix: LANGUAGE/LOCALES handling
-*	Bug fix: Loose comparison in stringify()
-
-3.1.2 (5 November 2013)
-*	Abandon .chm help format; Package API documentation in plain HTML;
-	(Launch lib/api/index.html in your browser)
-*	Deprecate BAIL in favor of HALT (default: TRUE)
-*	Revert to 3.1.0 autoload behavior; Add support for lowercase folder
-	names
-*	Allow Spring-style HTTP method overrides
-*	Add support for SQL Server-based sessions
-*	Capture full X-Forwarded-For header
-*	Add protection against malicious scripts; Extra check if file was really
-	uploaded
-*	Pass-thru page limit in return value of Cursor->paginate()
-*	Optimize code: Implement single-pass escaping
-*	Short circuit Jig->find() if source file is empty
-*	Bug fix: PHP globals passed by reference in hive() result (Issue #424)
-*	Bug fix: ZIP mime type incorrect behavior
-*	Bug fix: Jig->erase() filter malfunction
-*	Bug fix: Mongo->select() group
-*	Bug fix: Unknown bcrypt constant
-
-3.1.1 (13 October 2013)
-*	NEW: Support OpenID attribute exchange
-*	NEW: BAIL variable enables/disables continuance of execution on non-fatal
-	errors
-*	Deprecate BAIL in favor of HALT (default: FALSE)
-*	Add support for Oracle
-*	Mark cached queries in log (Feature Request #405)
-*	Implement Bcrypt->needs_reshash()
-*	Add entropy to SQL cache hash; Add uuid() method to DB backends
-*	Find real document root; Simplify debug paths
-*	Permit OpenID required fields to be declared as comma-separated string or
-	array
-*	Pass modified filename as argument to user-defined function in
-	Web->receive()
-*	Quote keys in optional SQL clauses (Issue #408)
-*	Allow UNLOAD to override fatal error detection (Issue #404)
-*	Mutex operator precedence error (Issue #406)
-*	Bug fix: exists() malfunction (Issue #401)
-*	Bug fix: Jig mapper triggers error when loading from CACHE (Issue #403)
-*	Bug fix: Array index check
-*	Bug fix: OpenID verified() return value
-*	Bug fix: Basket->find() should return a set of results (Issue #407);
-	Also implemented findone() for consistency with mappers
-*	Bug fix: PostgreSQL last insert ID (Issue #410)
-*	Bug fix: $port component URL overwritten by _socket()
-*	Bug fix: Calculation of elapsed time
-
-3.1.0 (20 August 2013)
-*	NEW: Web->filler() returns a chunk of text from the standard
-	Lorem Ipsum passage
-*	Change in behavior: Drop support for JSON serialization
-*	SQL->exec() now returns value of RETURNING clause
-*	Add support for $ttl argument in count() (Issue #393)
-*	Allow UI to be overridden by custom $path
-*	Return result of PDO primitives: begintransaction(), rollback(), and
-	commit()
-*	Full support for PHP 5.5
-*	Flush buffers only when DEBUG=0
-*	Support class->method, class::method, and lambda functions as
-	Web->basic() arguments
-*	Commit session on Basket->save()
-*	Optional enlargement in Image->resize()
-*	Support authentication on hosts running PHP-CGI
-*	Change visibility level of Cache properties
-*	Prevent ONERROR recursion
-*	Work around Apache pre-2.4 VirtualDocumentRoot bug
-*	Prioritize cURL in HTTP engine detection
-*	Bug fix: Minify tricky JS
-*	Bug fix: desktop() detection
-*	Bug fix: Double-slash on TEMP-relative path
-*	Bug fix: Cursor mapping of first() and last() records
-*	Bug fix: Premature end of Web->receive() on multiple files
-*	Bug fix: German umlaute to its corresponding grammatically-correct
-	equivalent
-
-3.0.9 (12 June 2013)
-*	NEW: Web->whois()
-*	NEW: Template <switch> <case> tags
-*	Improve CACHE consistency
-*	Case-insensitive MIME type detection
-*	Support pre-PHP 5.3.4 in Prefab->instance()
-*	Refactor isdesktop() and ismobile(); Add isbot()
-*	Add support for Markdown strike-through
-*	Work around ODBC's lack of quote() support
-*	Remove useless Prefab destructor
-*	Support multiple cache instances
-*	Bug fix: Underscores in OpenId keys mangled
-*	Refactor format()
-*	Numerous tweaks
-*	Bug fix: MongoId object not preserved
-*	Bug fix: Double-quotes included in lexicon() string (Issue #341)
-*	Bug fix: UTF-8 formatting mangled on Windows (Issue #342)
-*	Bug fix: Cache->load() error when CACHE is FALSE (Issue #344)
-*	Bug fix: send() ternary expression
-*	Bug fix: Country code constants
-
-3.0.8 (17 May 2013)
-*	NEW: Bcrypt lightweight hashing library\
-*	Return total number of records in superset in Cursor->paginate()
-*	ONERROR short-circuit (Enhancement #334)
-*	Apply quotes/backticks on DB identifiers
-*	Allow enabling/disabling of SQL log
-*	Normalize glob() behavior (Issue #330)
-*	Bug fix: mbstring 2-byte text truncation (Issue #325)
-*	Bug fix: Unsupported operand types (Issue #324)
-
-3.0.7 (2 May 2013)
-*	NEW: route() now allows an array of routing patterns as first argument;
-	support array as first argument of map()
-*	NEW: entropy() for calculating password strength (NIST 800-63)
-*	NEW: AGENT variable containing auto-detected HTTP user agent string
-*	NEW: ismobile() and isdesktop() methods
-*	NEW: Prefab class and descendants now accept constructor arguments
-*	Change in behavior: Cache->exists() now returns timestamp and TTL of
-	cache entry or FALSE if not found (Feature request #315)
-*	Preserve timestamp and TTL when updating cache entry (Feature request
-	#316)
-*	Improved currency formatting with C99 compliance
-*	Suppress unnecessary program halt at startup caused by misconfigured
-	server
-*	Add support for dashes in custom attribute names in templates
-*	Bug fix: Routing precedene (Issue #313)
-*	Bug fix: Remove Jig _id element from document property
-*	Bug fix: Web->rss() error when not enough items in the feed (Issue #299)
-*	Bug fix: Web engine fallback (Issue #300)
-*	Bug fix: <strong> and <em> formatting
-*	Bug fix: Text rendering of text with trailing punctuation (Issue #303)
-*	Bug fix: Incorrect regex in SMTP
-
-3.0.6 (31 Mar 2013)
-*	NEW: Image->crop()
-*	Modify documentation blocks for PHPDoc interoperability
-*	Allow user to control whether Base->rerouet() uses a permanent or
-	temporary redirect
-*	Allow JAR elements to be set individually
-*	Refactor DB\SQL\Mapper->insert() to cope with autoincrement fields
-*	Trigger error when captcha() font is missing
-*	Remove unnecessary markdown regex recursion
-*	Check for scalars instead of DB\SQL strings
-*	Implement more comprehensive diacritics table
-*	Add option for disabling 401 errors when basic auth() fails
-*	Add markdown syntax highlighting for Apache configuration
-*	Markdown->render() deprecated to remove dependency on UI variable;
-	Feature replaced by Markdown->convert() to enable translation from
-	markdown string to HTML
-*	Optimize factory() code of all data mappers
-*	Apply backticks on MySQL table names
-*	Bug fix: Routing failure when directory path contains a tilde (Issue #291)
-*	Bug fix: Incorrect markdown parsing of strong/em sequences and inline HTML
-*	Bug fix: Cached page not echoed (Issue #278)
-*	Bug fix: Object properties not escaped when rendering
-*	Bug fix: OpenID error response ignored
-*	Bug fix: memcache_get_extended_stats() timeout
-*	Bug fix: Base->set() doesn't pass TTL to Cache->set()
-*	Bug fix: Base->scrub() ignores pass-thru * argument (Issue #274)
-
-3.0.5 (16 Feb 2013)
-*	NEW: Markdown class with PHP, HTML, and .ini syntax highlighting support
-*	NEW: Options for caching of select() and find() results
-*	NEW: Web->acceptable()
-*	Add send() argument for forcing downloads
-*	Provide read() option for applying Unix LF as standard line ending
-*	Bypass lexicon() call if LANGUAGE is undefined
-*	Load fallback language dictionary if LANGUAGE is undefined
-*	map() now checks existence of class/methods for non-tokenized URLs
-*	Improve error reporting of non-existent Template methods
-*	Address output buffer issues on some servers
-*	Bug fix: Setting DEBUG to 0 won't suppress the stack trace when the
-	content type is application/json (Issue #257)
-*	Bug fix: Image dump/render additional arguments shifted
-*	Bug fix: ob_clean() causes buffer issues with zlib compression
-*	Bug fix: minify() fails when commenting CSS @ rules (Issue #251)
-*	Bug fix: Handling of commas inside quoted strings
-*	Bug fix: Glitch in stringify() handling of closures
-*	Bug fix: dry() in mappers returns TRUE despite being hydrated by
-	factory() (Issue #265)
-*	Bug fix: expect() not handling flags correctly
-*	Bug fix: weather() fails when server is unreachable
-
-3.0.4 (29 Jan 2013)
-*	NEW: Support for ICU/CLDR pluralization
-*	NEW: User-defined FALLBACK language
-*	NEW: minify() now recognizes CSS @import directives
-*	NEW: UTF->bom() returns byte order mark for UTF-8 encoding
-*	Expose SQL\Mapper->schema()
-*	Change in behavior: Send error response as JSON string if AJAX request is
-	detected
-*	Deprecated: afind*() methods
-*	Discard output buffer in favor of debug output
-*	Make _id available to Jig queries
-*	Magic class now implements ArrayAccess
-*	Abort execution on startup errors
-*	Suppress stack trace on DEBUG level 0
-*	Allow single = as equality operator in Jig query expressions
-*	Abort OpenID discovery if Web->request() fails
-*	Mimic PHP *RECURSION* in stringify()
-*	Modify Jig parser to allow wildcard-search using preg_match()
-*	Abort execution after error() execution
-*	Concatenate cached/uncached minify() iterations; Prevent spillover
-	caching of previous minify() result
-*	Work around obscure PHP session id regeneration bug
-*	Revise algorithm for Jig filter involving undefined fields (Issue #230)
-*	Use checkdnsrr() instead of gethostbyname() in DNSBL check
-*	Auto-adjust pagination to cursor boundaries
-*	Add Romanian diacritics
-*	Bug fix: Root namespace reference and sorting with undefined Jig fields
-*	Bug fix: Greedy receive() regex
-*	Bug fix: Default LANGUAGE always 'en'
-*	Bug fix: minify() hammers cache backend
-*	Bug fix: Previous values of primary keys not saved during factory()
-	instantiation
-*	Bug fix: Jig find() fails when search key is not present in all records
-*	Bug fix: Jig SORT_DESC (Issue #233)
-*	Bug fix: Error reporting (Issue #225)
-*	Bug fix: language() return value
-
-3.0.3 (29 Dec 2013)
-*	NEW: [ajax] and [sync] routing pattern modifiers
-*	NEW: Basket class (session-based pseudo-mapper, shopping cart, etc.)
-*	NEW: Test->message() method
-*	NEW: DB profiling via DB->log()
-*	NEW: Matrix->calendar()
-*	NEW: Audit->card() and Audit->mod10() for credit card verification
-*	NEW: Geo->weather()
-*	NEW: Base->relay() accepts comma-separated callbacks; but unlike
-	Base->chain(), result of previous callback becomes argument of the next
-*	Numerous performance tweaks
-*	Interoperability with new MongoClient class
-*	Web->request() now recognizes gzip and deflate encoding
-*	Differences in behavior of Web->request() engines rectified
-*	mutex() now uses an ID as argument (instead of filename to make it clear
-	that specified file is not the target being locked, but a primitive
-	cross-platform semaphore)
-*	DB\SQL\Mapper field _id now returned even in the absence of any
-	auto-increment field
-*	Magic class spinned off as a separate file
-*	ISO 3166-1 alpha-2 table updated
-*	Apache redirect emulation for PHP 5.4 CLI server mode
-*	Framework instance now passed as argument to any user-defined shutdown
-	function
-*	Cache engine now used as storage for Web->minify() output
-*	Flag added for enabling/disabling Image class filter history
-*	Bug fix: Trailing routing token consumes HTTP query
-*	Bug fix: LANGUAGE spills over to LOCALES setting
-*	Bug fix: Inconsistent dry() return value
-*	Bug fix: URL-decoding
-
-3.0.2 (23 Dec 2013)
-*	NEW: Syntax-highlighted stack traces via Base->highlight(); boolean
-	HIGHLIGHT global variable can be used to enable/disable this feature
-*	NEW: Template engine <ignore> tag
-*	NEW: Image->captcha()
-*	NEW: DNSBL-based spammer detection (ported from 2.x)
-*	NEW: paginate(), first(), and last() methods for data mappers
-*	NEW: X-HTTP-Method-Override header now recognized
-*	NEW: Base->chain() method for executing callbacks in succession
-*	NEW: HOST global variable; derived from either $_SERVER['SERVER_NAME'] or
-	gethostname()
-*	NEW: REALM global variable representing full canonical URI
-*	NEW: Auth plug-in
-*	NEW: Pingback plug-in (implements both Pingback 1.0 protocol client and
-	server)
-*	NEW: DEBUG verbosity can now reach up to level 3; Base->stringify() drills
-	down to object properties at this setting
-*	NEW: HTTP PATCH method added to recognized HTTP ReST methods
-*	Web->slug() now trims trailing dashes
-*	Web->request() now allows relative local URLs as argument
-*	Use of PARAMS in route handlers now unnecessary; framework now passes two
-	arguments to route handlers: the framework object instance and an array
-	containing the captured values of tokens in route patterns
-*	Standardized timeout settings among Web->request() backends
-*	Session IDs regenerated for additional security
-*	Automatic HTTP 404 responses by Base->call() now restricted to route
-	handlers
-*	Empty comments in ini-style files now parsed properly
-*	Use file_get_contents() in methods that don't involve high concurrency
-
-3.0.1 (14 Dec 2013)
-*	Major rewrite of much of the framework's core features

+ 0 - 621
frameworks/PHP/fat-free/src/COPYING

@@ -1,621 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-Version 3, 29 June 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
-
-Preamble
-
-The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
-When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and
-modification follow.
-
-TERMS AND CONDITIONS
-
-0. Definitions.
-
-"This License" refers to version 3 of the GNU General Public License.
-
-"Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-"The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
-To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-1. Source Code.
-
-The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
-A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-The Corresponding Source for a work in source code form is that
-same work.
-
-2. Basic Permissions.
-
-All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-4. Conveying Verbatim Copies.
-
-You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-
-You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-a) The work must carry prominent notices stating that you modified
-it, and giving a relevant date.
-
-b) The work must carry prominent notices stating that it is
-released under this License and any conditions added under section
-7. This requirement modifies the requirement in section 4 to
-"keep intact all notices".
-
-c) You must license the entire work, as a whole, under this
-License to anyone who comes into possession of a copy. This
-License will therefore apply, along with any applicable section 7
-additional terms, to the whole of the work, and all its parts,
-regardless of how they are packaged. This License gives no
-permission to license the work in any other way, but it does not
-invalidate such permission if you have separately received it.
-
-d) If the work has interactive user interfaces, each must display
-Appropriate Legal Notices; however, if the Program has interactive
-interfaces that do not display Appropriate Legal Notices, your
-work need not make them do so.
-
-A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-6. Conveying Non-Source Forms.
-
-You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-a) Convey the object code in, or embodied in, a physical product
-(including a physical distribution medium), accompanied by the
-Corresponding Source fixed on a durable physical medium
-customarily used for software interchange.
-
-b) Convey the object code in, or embodied in, a physical product
-(including a physical distribution medium), accompanied by a
-written offer, valid for at least three years and valid for as
-long as you offer spare parts or customer support for that product
-model, to give anyone who possesses the object code either (1) a
-copy of the Corresponding Source for all the software in the
-product that is covered by this License, on a durable physical
-medium customarily used for software interchange, for a price no
-more than your reasonable cost of physically performing this
-conveying of source, or (2) access to copy the
-Corresponding Source from a network server at no charge.
-
-c) Convey individual copies of the object code with a copy of the
-written offer to provide the Corresponding Source. This
-alternative is allowed only occasionally and noncommercially, and
-only if you received the object code with such an offer, in accord
-with subsection 6b.
-
-d) Convey the object code by offering access from a designated
-place (gratis or for a charge), and offer equivalent access to the
-Corresponding Source in the same way through the same place at no
-further charge. You need not require recipients to copy the
-Corresponding Source along with the object code. If the place to
-copy the object code is a network server, the Corresponding Source
-may be on a different server (operated by you or a third party)
-that supports equivalent copying facilities, provided you maintain
-clear directions next to the object code saying where to find the
-Corresponding Source. Regardless of what server hosts the
-Corresponding Source, you remain obligated to ensure that it is
-available for as long as needed to satisfy these requirements.
-
-e) Convey the object code using peer-to-peer transmission, provided
-you inform other peers where the object code and Corresponding
-Source of the work are being offered to the general public at no
-charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-"Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-7. Additional Terms.
-
-"Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-a) Disclaiming warranty or limiting liability differently from the
-terms of sections 15 and 16 of this License; or
-
-b) Requiring preservation of specified reasonable legal notices or
-author attributions in that material or in the Appropriate Legal
-Notices displayed by works containing it; or
-
-c) Prohibiting misrepresentation of the origin of that material, or
-requiring that modified versions of such material be marked in
-reasonable ways as different from the original version; or
-
-d) Limiting the use for publicity purposes of names of licensors or
-authors of the material; or
-
-e) Declining to grant rights under trademark law for use of some
-trade names, trademarks, or service marks; or
-
-f) Requiring indemnification of licensors and authors of that
-material by anyone who conveys the material (or modified versions of
-it) with contractual assumptions of liability to the recipient, for
-any liability that these contractual assumptions directly impose on
-those licensors and authors.
-
-All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-8. Termination.
-
-You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
-An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-11. Patents.
-
-A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
-A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-13. Use with the GNU Affero General Public License.
-
-Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS

+ 0 - 3087
frameworks/PHP/fat-free/src/base.php

@@ -1,3087 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-//! Factory class for single-instance objects
-abstract class Prefab {
-
-	/**
-	*	Return class instance
-	*	@return static
-	**/
-	static function instance() {
-		if (!Registry::exists($class=get_called_class())) {
-			$ref=new Reflectionclass($class);
-			$args=func_get_args();
-			Registry::set($class,
-				$args?$ref->newinstanceargs($args):new $class);
-		}
-		return Registry::get($class);
-	}
-
-}
-
-//! Base structure
-final class Base extends Prefab implements ArrayAccess {
-
-	//@{ Framework details
-	const
-		PACKAGE='Fat-Free Framework',
-		VERSION='3.5.0-Release';
-	//@}
-
-	//@{ HTTP status codes (RFC 2616)
-	const
-		HTTP_100='Continue',
-		HTTP_101='Switching Protocols',
-		HTTP_200='OK',
-		HTTP_201='Created',
-		HTTP_202='Accepted',
-		HTTP_203='Non-Authorative Information',
-		HTTP_204='No Content',
-		HTTP_205='Reset Content',
-		HTTP_206='Partial Content',
-		HTTP_300='Multiple Choices',
-		HTTP_301='Moved Permanently',
-		HTTP_302='Found',
-		HTTP_303='See Other',
-		HTTP_304='Not Modified',
-		HTTP_305='Use Proxy',
-		HTTP_307='Temporary Redirect',
-		HTTP_400='Bad Request',
-		HTTP_401='Unauthorized',
-		HTTP_402='Payment Required',
-		HTTP_403='Forbidden',
-		HTTP_404='Not Found',
-		HTTP_405='Method Not Allowed',
-		HTTP_406='Not Acceptable',
-		HTTP_407='Proxy Authentication Required',
-		HTTP_408='Request Timeout',
-		HTTP_409='Conflict',
-		HTTP_410='Gone',
-		HTTP_411='Length Required',
-		HTTP_412='Precondition Failed',
-		HTTP_413='Request Entity Too Large',
-		HTTP_414='Request-URI Too Long',
-		HTTP_415='Unsupported Media Type',
-		HTTP_416='Requested Range Not Satisfiable',
-		HTTP_417='Expectation Failed',
-		HTTP_500='Internal Server Error',
-		HTTP_501='Not Implemented',
-		HTTP_502='Bad Gateway',
-		HTTP_503='Service Unavailable',
-		HTTP_504='Gateway Timeout',
-		HTTP_505='HTTP Version Not Supported';
-	//@}
-
-	const
-		//! Mapped PHP globals
-		GLOBALS='GET|POST|COOKIE|REQUEST|SESSION|FILES|SERVER|ENV',
-		//! HTTP verbs
-		VERBS='GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT',
-		//! Default directory permissions
-		MODE=0755,
-		//! Syntax highlighting stylesheet
-		CSS='code.css';
-
-	//@{ HTTP request types
-	const
-		REQ_SYNC=1,
-		REQ_AJAX=2;
-	//@}
-
-	//@{ Error messages
-	const
-		E_Pattern='Invalid routing pattern: %s',
-		E_Named='Named route does not exist: %s',
-		E_Fatal='Fatal error: %s',
-		E_Open='Unable to open %s',
-		E_Routes='No routes specified',
-		E_Class='Invalid class %s',
-		E_Method='Invalid method %s',
-		E_Hive='Invalid hive key %s';
-	//@}
-
-	private
-		//! Globals
-		$hive,
-		//! Initial settings
-		$init,
-		//! Language lookup sequence
-		$languages,
-		//! Default fallback language
-		$fallback='en';
-
-	/**
-	*	Sync PHP global with corresponding hive key
-	*	@return array
-	*	@param $key string
-	**/
-	function sync($key) {
-		return $this->hive[$key]=&$GLOBALS['_'.$key];
-	}
-
-	/**
-	*	Return the parts of specified hive key
-	*	@return array
-	*	@param $key string
-	**/
-	private function cut($key) {
-		return preg_split('/\[\h*[\'"]?(.+?)[\'"]?\h*\]|(->)|\./',
-			$key,NULL,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
-	}
-
-	/**
-	*	Replace tokenized URL with available token values
-	*	@return string
-	*	@param $url array|string
-	*	@param $params array
-	**/
-	function build($url,$params=array()) {
-		$params+=$this->hive['PARAMS'];
-		if (is_array($url))
-			foreach ($url as &$var) {
-				$var=$this->build($var,$params);
-				unset($var);
-			}
-		else {
-			$i=0;
-			$url=preg_replace_callback('/@(\w+)|\*/',
-				function($match) use(&$i,$params) {
-					$i++;
-					if (isset($match[1]) &&
-						array_key_exists($match[1],$params))
-						return $params[$match[1]];
-					return array_key_exists($i,$params)?
-						$params[$i]:
-						$match[0];
-				},$url);
-		}
-		return $url;
-	}
-
-	/**
-	*	assemble url from alias name
-	*	@return NULL
-	*	@param $name string
-	*	@param $params array|string
-	**/
-	function alias($name,$params=array()) {
-		if (!is_array($params))
-			$params=$this->parse($params);
-		if (empty($this->hive['ALIASES'][$name]))
-			user_error(sprintf(self::E_Named,$name),E_USER_ERROR);
-		$url=$this->build($this->hive['ALIASES'][$name],$params);
-		return $url;
-	}
-
-	/**
-	*	Parse string containing key-value pairs
-	*	@return array
-	*	@param $str string
-	**/
-	function parse($str) {
-		preg_match_all('/(\w+)\h*=\h*(.+?)(?=,|$)/',
-			$str,$pairs,PREG_SET_ORDER);
-		$out=array();
-		foreach ($pairs as $pair)
-			$out[$pair[1]]=trim($pair[2]);
-		return $out;
-	}
-
-	/**
-	*	Convert JS-style token to PHP expression
-	*	@return string
-	*	@param $str string
-	**/
-	function compile($str) {
-		$fw=$this;
-		return preg_replace_callback(
-			'/(?<!\w)@(\w(?:[\w\.\[\]\(]|\->|::)*)/',
-			function($var) use($fw) {
-				return '$'.preg_replace_callback(
-					'/\.(\w+)\(|\.(\w+)|\[((?:[^\[\]]*|(?R))*)\]/',
-					function($expr) use($fw) {
-						return $expr[1]?
-							((function_exists($expr[1])?
-								('.'.$expr[1]):
-								('['.var_export($expr[1],TRUE).']')).'('):
-							('['.var_export(
-								isset($expr[3])?
-									$fw->compile($expr[3]):
-									(ctype_digit($expr[2])?
-										(int)$expr[2]:
-										$expr[2]),TRUE).']');
-					},
-					$var[1]
-				);
-			},
-			$str
-		);
-	}
-
-	/**
-	*	Get hive key reference/contents; Add non-existent hive keys,
-	*	array elements, and object properties by default
-	*	@return mixed
-	*	@param $key string
-	*	@param $add bool
-	**/
-	function &ref($key,$add=TRUE) {
-		$null=NULL;
-		$parts=$this->cut($key);
-		if ($parts[0]=='SESSION') {
-			@session_start();
-			$this->sync('SESSION');
-		}
-		elseif (!preg_match('/^\w+$/',$parts[0]))
-			user_error(sprintf(self::E_Hive,$this->stringify($key)),
-				E_USER_ERROR);
-		if ($add)
-			$var=&$this->hive;
-		else
-			$var=$this->hive;
-		$obj=FALSE;
-		foreach ($parts as $part)
-			if ($part=='->')
-				$obj=TRUE;
-			elseif ($obj) {
-				$obj=FALSE;
-				if (!is_object($var))
-					$var=new stdclass;
-				if ($add || property_exists($var,$part))
-					$var=&$var->$part;
-				else {
-					$var=&$null;
-					break;
-				}
-			}
-			else {
-				if (!is_array($var))
-					$var=array();
-				if ($add || array_key_exists($part,$var))
-					$var=&$var[$part];
-				else {
-					$var=&$null;
-					break;
-				}
-			}
-		if ($parts[0]=='ALIASES')
-			$var=$this->build($var);
-		return $var;
-	}
-
-	/**
-	*	Return TRUE if hive key is set
-	*	(or return timestamp and TTL if cached)
-	*	@return bool
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	function exists($key,&$val=NULL) {
-		$val=$this->ref($key,FALSE);
-		return isset($val)?
-			TRUE:
-			(Cache::instance()->exists($this->hash($key).'.var',$val)?:FALSE);
-	}
-
-	/**
-	*	Return TRUE if hive key is empty and not cached
-	*	@return bool
-	*	@param $key string
-	**/
-	function devoid($key) {
-		$val=$this->ref($key,FALSE);
-		return empty($val) &&
-			(!Cache::instance()->exists($this->hash($key).'.var',$val) ||
-				!$val);
-	}
-
-	/**
-	*	Bind value to hive key
-	*	@return mixed
-	*	@param $key string
-	*	@param $val mixed
-	*	@param $ttl int
-	**/
-	function set($key,$val,$ttl=0) {
-		$time=time();
-		if (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
-			$this->set('REQUEST'.$expr[2],$val);
-			if ($expr[1]=='COOKIE') {
-				$parts=$this->cut($key);
-				$jar=$this->unserialize($this->serialize($this->hive['JAR']));
-				if ($ttl)
-					$jar['expire']=$time+$ttl;
-				call_user_func_array('setcookie',array($parts[1],$val)+$jar);
-				return $val;
-			}
-		}
-		else switch ($key) {
-		case 'CACHE':
-			$val=Cache::instance()->load($val,TRUE);
-			break;
-		case 'ENCODING':
-			ini_set('default_charset',$val);
-			if (extension_loaded('mbstring'))
-				mb_internal_encoding($val);
-			break;
-		case 'FALLBACK':
-			$this->fallback=$val;
-			$lang=$this->language($this->hive['LANGUAGE']);
-		case 'LANGUAGE':
-			if (!isset($lang))
-				$val=$this->language($val);
-			$lex=$this->lexicon($this->hive['LOCALES']);
-		case 'LOCALES':
-			if (isset($lex) || $lex=$this->lexicon($val))
-				$this->mset($lex,$this->hive['PREFIX'],$ttl);
-			break;
-		case 'TZ':
-			date_default_timezone_set($val);
-			break;
-		}
-		$ref=&$this->ref($key);
-		$ref=$val;
-		if (preg_match('/^JAR\b/',$key)) {
-			$jar=$this->unserialize($this->serialize($this->hive['JAR']));
-			$jar['expire']-=$time;
-			call_user_func_array('session_set_cookie_params',$jar);
-		}
-		$cache=Cache::instance();
-		if ($cache->exists($hash=$this->hash($key).'.var') || $ttl)
-			// Persist the key-value pair
-			$cache->set($hash,$val,$ttl);
-		return $ref;
-	}
-
-	/**
-	*	Retrieve contents of hive key
-	*	@return mixed
-	*	@param $key string
-	*	@param $args string|array
-	**/
-	function get($key,$args=NULL) {
-		if (is_string($val=$this->ref($key,FALSE)) && !is_null($args))
-			return call_user_func_array(
-				array($this,'format'),
-				array_merge(array($val),is_array($args)?$args:array($args))
-			);
-		if (is_null($val)) {
-			// Attempt to retrieve from cache
-			if (Cache::instance()->exists($this->hash($key).'.var',$data))
-				return $data;
-		}
-		return $val;
-	}
-
-	/**
-	*	Unset hive key
-	*	@return NULL
-	*	@param $key string
-	**/
-	function clear($key) {
-		// Normalize array literal
-		$cache=Cache::instance();
-		$parts=$this->cut($key);
-		if ($key=='CACHE')
-			// Clear cache contents
-			$cache->reset();
-		elseif (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
-			$this->clear('REQUEST'.$expr[2]);
-			if ($expr[1]=='COOKIE') {
-				$parts=$this->cut($key);
-				$jar=$this->hive['JAR'];
-				$jar['expire']=strtotime('-1 year');
-				call_user_func_array('setcookie',
-					array_merge(array($parts[1],''),$jar));
-				unset($_COOKIE[$parts[1]]);
-			}
-		}
-		elseif ($parts[0]=='SESSION') {
-			@session_start();
-			if (empty($parts[1])) {
-				// End session
-				session_unset();
-				session_destroy();
-				unset($_COOKIE[session_name()]);
-				header_remove('Set-Cookie');
-			}
-			$this->sync('SESSION');
-		}
-		if (!isset($parts[1]) && array_key_exists($parts[0],$this->init))
-			// Reset global to default value
-			$this->hive[$parts[0]]=$this->init[$parts[0]];
-		else {
-			eval('unset('.$this->compile('@this->hive.'.$key).');');
-			if ($parts[0]=='SESSION') {
-				session_commit();
-				session_start();
-			}
-			if ($cache->exists($hash=$this->hash($key).'.var'))
-				// Remove from cache
-				$cache->clear($hash);
-		}
-	}
-
-	/**
-	*	Return TRUE if hive variable is 'on'
-	*	@return bool
-	*	@param $key string
-	**/
-	function checked($key) {
-		$ref=&$this->ref($key);
-		return $ref=='on';
-	}
-
-	/**
-	*	Return TRUE if property has public visibility
-	*	@return bool
-	*	@param $obj object
-	*	@param $key string
-	**/
-	function visible($obj,$key) {
-		if (property_exists($obj,$key)) {
-			$ref=new ReflectionProperty(get_class($obj),$key);
-			$out=$ref->ispublic();
-			unset($ref);
-			return $out;
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Multi-variable assignment using associative array
-	*	@return NULL
-	*	@param $vars array
-	*	@param $prefix string
-	*	@param $ttl int
-	**/
-	function mset(array $vars,$prefix='',$ttl=0) {
-		foreach ($vars as $key=>$val)
-			$this->set($prefix.$key,$val,$ttl);
-	}
-
-	/**
-	*	Publish hive contents
-	*	@return array
-	**/
-	function hive() {
-		return $this->hive;
-	}
-
-	/**
-	*	Copy contents of hive variable to another
-	*	@return mixed
-	*	@param $src string
-	*	@param $dst string
-	**/
-	function copy($src,$dst) {
-		$ref=&$this->ref($dst);
-		return $ref=$this->ref($src,FALSE);
-	}
-
-	/**
-	*	Concatenate string to hive string variable
-	*	@return string
-	*	@param $key string
-	*	@param $val string
-	**/
-	function concat($key,$val) {
-		$ref=&$this->ref($key);
-		$ref.=$val;
-		return $ref;
-	}
-
-	/**
-	*	Swap keys and values of hive array variable
-	*	@return array
-	*	@param $key string
-	*	@public
-	**/
-	function flip($key) {
-		$ref=&$this->ref($key);
-		return $ref=array_combine(array_values($ref),array_keys($ref));
-	}
-
-	/**
-	*	Add element to the end of hive array variable
-	*	@return mixed
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	function push($key,$val) {
-		$ref=&$this->ref($key);
-		$ref[] = $val;
-		return $val;
-	}
-
-	/**
-	*	Remove last element of hive array variable
-	*	@return mixed
-	*	@param $key string
-	**/
-	function pop($key) {
-		$ref=&$this->ref($key);
-		return array_pop($ref);
-	}
-
-	/**
-	*	Add element to the beginning of hive array variable
-	*	@return mixed
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	function unshift($key,$val) {
-		$ref=&$this->ref($key);
-		array_unshift($ref,$val);
-		return $val;
-	}
-
-	/**
-	*	Remove first element of hive array variable
-	*	@return mixed
-	*	@param $key string
-	**/
-	function shift($key) {
-		$ref=&$this->ref($key);
-		return array_shift($ref);
-	}
-
-	/**
-	*	Merge array with hive array variable
-	*	@return array
-	*	@param $key string
-	*	@param $src string|array
-	**/
-	function merge($key,$src) {
-		$ref=&$this->ref($key);
-		return array_merge($ref,is_string($src)?$this->hive[$src]:$src);
-	}
-
-	/**
-	*	Convert backslashes to slashes
-	*	@return string
-	*	@param $str string
-	**/
-	function fixslashes($str) {
-		return $str?strtr($str,'\\','/'):$str;
-	}
-
-	/**
-	*	Split comma-, semi-colon, or pipe-separated string
-	*	@return array
-	*	@param $str string
-	*	@param $noempty bool
-	**/
-	function split($str,$noempty=TRUE) {
-		return array_map('trim',
-			preg_split('/[,;|]/',$str,0,$noempty?PREG_SPLIT_NO_EMPTY:0));
-	}
-
-	/**
-	*	Convert PHP expression/value to compressed exportable string
-	*	@return string
-	*	@param $arg mixed
-	*	@param $stack array
-	**/
-	function stringify($arg,array $stack=NULL) {
-		if ($stack) {
-			foreach ($stack as $node)
-				if ($arg===$node)
-					return '*RECURSION*';
-		}
-		else
-			$stack=array();
-		switch (gettype($arg)) {
-			case 'object':
-				$str='';
-				foreach (get_object_vars($arg) as $key=>$val)
-					$str.=($str?',':'').
-						var_export($key,TRUE).'=>'.
-						$this->stringify($val,
-							array_merge($stack,array($arg)));
-				return get_class($arg).'::__set_state(array('.$str.'))';
-			case 'array':
-				$str='';
-				$num=isset($arg[0]) &&
-					ctype_digit(implode('',array_keys($arg)));
-				foreach ($arg as $key=>$val)
-					$str.=($str?',':'').
-						($num?'':(var_export($key,TRUE).'=>')).
-						$this->stringify($val,
-							array_merge($stack,array($arg)));
-				return 'array('.$str.')';
-			default:
-				return var_export($arg,TRUE);
-		}
-	}
-
-	/**
-	*	Flatten array values and return as CSV string
-	*	@return string
-	*	@param $args array
-	**/
-	function csv(array $args) {
-		return implode(',',array_map('stripcslashes',
-			array_map(array($this,'stringify'),$args)));
-	}
-
-	/**
-	*	Convert snakecase string to camelcase
-	*	@return string
-	*	@param $str string
-	**/
-	function camelcase($str) {
-		return preg_replace_callback(
-			'/_(\w)/',
-			function($match) {
-				return strtoupper($match[1]);
-			},
-			$str
-		);
-	}
-
-	/**
-	*	Convert camelcase string to snakecase
-	*	@return string
-	*	@param $str string
-	**/
-	function snakecase($str) {
-		return strtolower(preg_replace('/[[:upper:]]/','_\0',$str));
-	}
-
-	/**
-	*	Return -1 if specified number is negative, 0 if zero,
-	*	or 1 if the number is positive
-	*	@return int
-	*	@param $num mixed
-	**/
-	function sign($num) {
-		return $num?($num/abs($num)):0;
-	}
-
-	/**
-	*	Convert class constants to array
-	*	@return array
-	*	@param $class object|string
-	*	@param $prefix string
-	**/
-	function constants($class,$prefix='') {
-		$ref=new ReflectionClass($class);
-		$out=array();
-		foreach (preg_grep('/^'.$prefix.'/',array_keys($ref->getconstants()))
-			as $val) {
-			$out[$key=substr($val,strlen($prefix))]=
-				constant((is_object($class)?get_class($class):$class).'::'.$prefix.$key);
-		}
-		unset($ref);
-		return $out;
-	}
-
-	/**
-	*	Generate 64bit/base36 hash
-	*	@return string
-	*	@param $str
-	**/
-	function hash($str) {
-		return str_pad(base_convert(
-			substr(sha1($str),-16),16,36),11,'0',STR_PAD_LEFT);
-	}
-
-	/**
-	*	Return Base64-encoded equivalent
-	*	@return string
-	*	@param $data string
-	*	@param $mime string
-	**/
-	function base64($data,$mime) {
-		return 'data:'.$mime.';base64,'.base64_encode($data);
-	}
-
-	/**
-	*	Convert special characters to HTML entities
-	*	@return string
-	*	@param $str string
-	**/
-	function encode($str) {
-		return @htmlspecialchars($str,$this->hive['BITMASK'],
-			$this->hive['ENCODING'])?:$this->scrub($str);
-	}
-
-	/**
-	*	Convert HTML entities back to characters
-	*	@return string
-	*	@param $str string
-	**/
-	function decode($str) {
-		return htmlspecialchars_decode($str,$this->hive['BITMASK']);
-	}
-
-	/**
-	*	Invoke callback recursively for all data types
-	*	@return mixed
-	*	@param $arg mixed
-	*	@param $func callback
-	*	@param $stack array
-	**/
-	function recursive($arg,$func,$stack=NULL) {
-		if ($stack) {
-			foreach ($stack as $node)
-				if ($arg===$node)
-					return $arg;
-		}
-		else
-			$stack=array();
-		switch (gettype($arg)) {
-			case 'object':
-				if (method_exists('ReflectionClass','iscloneable')) {
-					$ref=new ReflectionClass($arg);
-					if ($ref->iscloneable()) {
-						$arg=clone($arg);
-						$cast=is_a($arg,'IteratorAggregate')?
-							iterator_to_array($arg):get_object_vars($arg);
-						foreach ($cast as $key=>$val)
-							$arg->$key=$this->recursive(
-								$val,$func,array_merge($stack,array($arg)));
-					}
-				}
-				return $arg;
-			case 'array':
-				$copy=array();
-				foreach ($arg as $key=>$val)
-					$copy[$key]=$this->recursive($val,$func,
-						array_merge($stack,array($arg)));
-				return $copy;
-		}
-		return $func($arg);
-	}
-
-	/**
-	*	Remove HTML tags (except those enumerated) and non-printable
-	*	characters to mitigate XSS/code injection attacks
-	*	@return mixed
-	*	@param $arg mixed
-	*	@param $tags string
-	**/
-	function clean($arg,$tags=NULL) {
-		$fw=$this;
-		return $this->recursive($arg,
-			function($val) use($fw,$tags) {
-				if ($tags!='*')
-					$val=trim(strip_tags($val,
-						'<'.implode('><',$fw->split($tags)).'>'));
-				return trim(preg_replace(
-					'/[\x00-\x08\x0B\x0C\x0E-\x1F]/','',$val));
-			}
-		);
-	}
-
-	/**
-	*	Similar to clean(), except that variable is passed by reference
-	*	@return mixed
-	*	@param $var mixed
-	*	@param $tags string
-	**/
-	function scrub(&$var,$tags=NULL) {
-		return $var=$this->clean($var,$tags);
-	}
-
-	/**
-	*	Return locale-aware formatted string
-	*	@return string
-	**/
-	function format() {
-		$args=func_get_args();
-		$val=array_shift($args);
-		// Get formatting rules
-		$conv=localeconv();
-		return preg_replace_callback(
-			'/\{(?P<pos>\d+)\s*(?:,\s*(?P<type>\w+)\s*'.
-			'(?:,\s*(?P<mod>(?:\w+(?:\s*\{.+?\}\s*,?)?)*)'.
-			'(?:,\s*(?P<prop>.+?))?)?)?\}/',
-			function($expr) use($args,$conv) {
-				extract($expr);
-				extract($conv);
-				if (!array_key_exists($pos,$args))
-					return $expr[0];
-				if (isset($type))
-					switch ($type) {
-						case 'plural':
-							preg_match_all('/(?<tag>\w+)'.
-								'(?:\s*\{\s*(?<data>.+?)\s*\})/',
-								$mod,$matches,PREG_SET_ORDER);
-							$ord=array('zero','one','two');
-							foreach ($matches as $match) {
-								extract($match);
-								if (isset($ord[$args[$pos]]) &&
-									$tag==$ord[$args[$pos]] || $tag=='other')
-									return str_replace('#',$args[$pos],$data);
-							}
-						case 'number':
-							if (isset($mod))
-								switch ($mod) {
-									case 'integer':
-										return number_format(
-											$args[$pos],0,'',$thousands_sep);
-									case 'currency':
-										if (function_exists('money_format'))
-											return money_format(
-												'%n',$args[$pos]);
-										$fmt=array(
-											0=>'(nc)',1=>'(n c)',
-											2=>'(nc)',10=>'+nc',
-											11=>'+n c',12=>'+ nc',
-											20=>'nc+',21=>'n c+',
-											22=>'nc +',30=>'n+c',
-											31=>'n +c',32=>'n+ c',
-											40=>'nc+',41=>'n c+',
-											42=>'nc +',100=>'(cn)',
-											101=>'(c n)',102=>'(cn)',
-											110=>'+cn',111=>'+c n',
-											112=>'+ cn',120=>'cn+',
-											121=>'c n+',122=>'cn +',
-											130=>'+cn',131=>'+c n',
-											132=>'+ cn',140=>'c+n',
-											141=>'c+ n',142=>'c +n'
-										);
-										if ($args[$pos]<0) {
-											$sgn=$negative_sign;
-											$pre='n';
-										}
-										else {
-											$sgn=$positive_sign;
-											$pre='p';
-										}
-										return str_replace(
-											array('+','n','c'),
-											array($sgn,number_format(
-												abs($args[$pos]),
-												$frac_digits,
-												$decimal_point,
-												$thousands_sep),
-												$currency_symbol),
-											$fmt[(int)(
-												(${$pre.'_cs_precedes'}%2).
-												(${$pre.'_sign_posn'}%5).
-												(${$pre.'_sep_by_space'}%3)
-											)]
-										);
-									case 'percent':
-										return number_format(
-											$args[$pos]*100,0,$decimal_point,
-											$thousands_sep).'%';
-									case 'decimal':
-										return number_format(
-											$args[$pos],$prop,$decimal_point,
-												$thousands_sep);
-								}
-							break;
-						case 'date':
-							if (empty($mod) || $mod=='short')
-								$prop='%x';
-							elseif ($mod=='long')
-								$prop='%A, %d %B %Y';
-							return strftime($prop,$args[$pos]);
-						case 'time':
-							if (empty($mod) || $mod=='short')
-								$prop='%X';
-							return strftime($prop,$args[$pos]);
-						default:
-							return $expr[0];
-					}
-				return $args[$pos];
-			},
-			$val
-		);
-	}
-
-	/**
-	*	Assign/auto-detect language
-	*	@return string
-	*	@param $code string
-	**/
-	function language($code) {
-		$code=preg_replace('/\h+|;q=[0-9.]+/','',$code);
-		$code.=($code?',':'').$this->fallback;
-		$this->languages=array();
-		foreach (array_reverse(explode(',',$code)) as $lang) {
-			if (preg_match('/^(\w{2})(?:-(\w{2}))?\b/i',$lang,$parts)) {
-				// Generic language
-				array_unshift($this->languages,$parts[1]);
-				if (isset($parts[2])) {
-					// Specific language
-					$parts[0]=$parts[1].'-'.($parts[2]=strtoupper($parts[2]));
-					array_unshift($this->languages,$parts[0]);
-				}
-			}
-		}
-		$this->languages=array_unique($this->languages);
-		$locales=array();
-		$windows=preg_match('/^win/i',PHP_OS);
-		foreach ($this->languages as $locale) {
-			if ($windows) {
-				$parts=explode('-',$locale);
-				$locale=@constant('ISO::LC_'.$parts[0]);
-				if (isset($parts[1]) &&
-					$country=@constant('ISO::CC_'.strtolower($parts[1])))
-					$locale.='-'.$country;
-			}
-			$locales[]=$locale;
-			$locales[]=$locale.'.'.ini_get('default_charset');
-		}
-		setlocale(LC_ALL,str_replace('-','_',$locales));
-		return implode(',',$this->languages);
-	}
-
-	/**
-	*	Return lexicon entries
-	*	@return array
-	*	@param $path string
-	**/
-	function lexicon($path) {
-		$lex=array();
-		foreach ($this->languages?:explode(',',$this->fallback) as $lang)
-			foreach ($this->split($path) as $dir)
-				if ((is_file($file=($base=$dir.$lang).'.php') ||
-					is_file($file=$base.'.php')) &&
-					is_array($dict=require($file)))
-					$lex+=$dict;
-				elseif (is_file($file=$base.'.ini')) {
-					preg_match_all(
-						'/(?<=^|\n)(?:'.
-							'\[(?<prefix>.+?)\]|'.
-							'(?<lval>[^\h\r\n;].*?)\h*=\h*'.
-							'(?<rval>(?:\\\\\h*\r?\n|.+?)*)'.
-						')(?=\r?\n|$)/',
-						$this->read($file),$matches,PREG_SET_ORDER);
-					if ($matches) {
-						$prefix='';
-						foreach ($matches as $match)
-							if ($match['prefix'])
-								$prefix=$match['prefix'].'.';
-							elseif (!array_key_exists(
-								$key=$prefix.$match['lval'],$lex))
-								$lex[$key]=trim(preg_replace(
-									'/\\\\\h*\r?\n/','',$match['rval']));
-					}
-				}
-		return $lex;
-	}
-
-	/**
-	*	Return string representation of PHP value
-	*	@return string
-	*	@param $arg mixed
-	**/
-	function serialize($arg) {
-		switch (strtolower($this->hive['SERIALIZER'])) {
-			case 'igbinary':
-				return igbinary_serialize($arg);
-			default:
-				return serialize($arg);
-		}
-	}
-
-	/**
-	*	Return PHP value derived from string
-	*	@return string
-	*	@param $arg mixed
-	**/
-	function unserialize($arg) {
-		switch (strtolower($this->hive['SERIALIZER'])) {
-			case 'igbinary':
-				return igbinary_unserialize($arg);
-			default:
-				return unserialize($arg);
-		}
-	}
-
-	/**
-	*	Send HTTP status header; Return text equivalent of status code
-	*	@return string
-	*	@param $code int
-	**/
-	function status($code) {
-		$reason=@constant('self::HTTP_'.$code);
-		if (PHP_SAPI!='cli')
-			header($_SERVER['SERVER_PROTOCOL'].' '.$code.' '.$reason);
-		return $reason;
-	}
-
-	/**
-	*	Send cache metadata to HTTP client
-	*	@return NULL
-	*	@param $secs int
-	**/
-	function expire($secs=0) {
-		if (PHP_SAPI!='cli') {
-			header('X-Content-Type-Options: nosniff');
-			header('X-Frame-Options: '.$this->hive['XFRAME']);
-			header('X-Powered-By: '.$this->hive['PACKAGE']);
-			header('X-XSS-Protection: 1; mode=block');
-			if ($secs) {
-				$time=microtime(TRUE);
-				header_remove('Pragma');
-				header('Expires: '.gmdate('r',$time+$secs));
-				header('Cache-Control: max-age='.$secs);
-				header('Last-Modified: '.gmdate('r'));
-			}
-			else
-				header('Cache-Control: no-cache, no-store, must-revalidate');
-		}
-	}
-
-	/**
-	*	Return HTTP user agent
-	*	@return string
-	**/
-	function agent() {
-		$headers=$this->hive['HEADERS'];
-		return isset($headers['X-Operamini-Phone-UA'])?
-			$headers['X-Operamini-Phone-UA']:
-			(isset($headers['X-Skyfire-Phone'])?
-				$headers['X-Skyfire-Phone']:
-				(isset($headers['User-Agent'])?
-					$headers['User-Agent']:''));
-	}
-
-	/**
-	*	Return TRUE if XMLHttpRequest detected
-	*	@return bool
-	**/
-	function ajax() {
-		$headers=$this->hive['HEADERS'];
-		return isset($headers['X-Requested-With']) &&
-			$headers['X-Requested-With']=='XMLHttpRequest';
-	}
-
-	/**
-	*	Sniff IP address
-	*	@return string
-	**/
-	function ip() {
-		$headers=$this->hive['HEADERS'];
-		return isset($headers['Client-IP'])?
-			$headers['Client-IP']:
-			(isset($headers['X-Forwarded-For'])?
-				$headers['X-Forwarded-For']:
-				(isset($_SERVER['REMOTE_ADDR'])?
-					$_SERVER['REMOTE_ADDR']:''));
-	}
-
-	/**
-	*	Return formatted stack trace
-	*	@return string
-	*	@param $trace array|NULL
-	**/
-	function trace(array $trace=NULL) {
-		if (!$trace) {
-			$trace=debug_backtrace(FALSE);
-			$frame=$trace[0];
-			if (isset($frame['file']) && $frame['file']==__FILE__)
-				array_shift($trace);
-		}
-		$debug=$this->hive['DEBUG'];
-		$trace=array_filter(
-			$trace,
-			function($frame) use($debug) {
-				return $debug && isset($frame['file']) &&
-					($frame['file']!=__FILE__ || $debug>1) &&
-					(empty($frame['function']) ||
-					!preg_match('/^(?:(?:trigger|user)_error|'.
-						'__call|call_user_func)/',$frame['function']));
-			}
-		);
-		$out='';
-		$eol="\n";
-		// Analyze stack trace
-		foreach ($trace as $frame) {
-			$line='';
-			if (isset($frame['class']))
-				$line.=$frame['class'].$frame['type'];
-			if (isset($frame['function']))
-				$line.=$frame['function'].'('.
-					($debug>2 && isset($frame['args'])?
-						$this->csv($frame['args']):'').')';
-			$src=$this->fixslashes(str_replace($_SERVER['DOCUMENT_ROOT'].
-				'/','',$frame['file'])).':'.$frame['line'];
-			$out.='['.$src.'] '.$line.$eol;
-		}
-		return $out;
-	}
-
-	/**
-	*	Log error; Execute ONERROR handler if defined, else display
-	*	default error page (HTML for synchronous requests, JSON string
-	*	for AJAX requests)
-	*	@return NULL
-	*	@param $code int
-	*	@param $text string
-	*	@param $trace array
-	**/
-	function error($code,$text='',array $trace=NULL) {
-		$prior=$this->hive['ERROR'];
-		$header=$this->status($code);
-		$req=$this->hive['VERB'].' '.$this->hive['PATH'];
-		if (!$text)
-			$text='HTTP '.$code.' ('.$req.')';
-		error_log($text);
-		$trace=$this->trace($trace);
-		foreach (explode("\n",$trace) as $nexus)
-			if ($nexus)
-				error_log($nexus);
-		if ($highlight=PHP_SAPI!='cli' && !$this->hive['AJAX'] &&
-			$this->hive['HIGHLIGHT'] && is_file($css=__DIR__.'/'.self::CSS))
-			$trace=$this->highlight($trace);
-		$this->hive['ERROR']=array(
-			'status'=>$header,
-			'code'=>$code,
-			'text'=>$text,
-			'trace'=>$trace
-		);
-		$handler=$this->hive['ONERROR'];
-		$this->hive['ONERROR']=NULL;
-		$eol="\n";
-		if ((!$handler ||
-			$this->call($handler,array($this,$this->hive['PARAMS']),
-				'beforeroute,afterroute')===FALSE) &&
-			!$prior && PHP_SAPI!='cli' && !$this->hive['QUIET'])
-			echo $this->hive['AJAX']?
-				json_encode($this->hive['ERROR']):
-				('<!DOCTYPE html>'.$eol.
-				'<html>'.$eol.
-				'<head>'.
-					'<title>'.$code.' '.$header.'</title>'.
-					($highlight?
-						('<style>'.$this->read($css).'</style>'):'').
-				'</head>'.$eol.
-				'<body>'.$eol.
-					'<h1>'.$header.'</h1>'.$eol.
-					'<p>'.$this->encode($text?:$req).'</p>'.$eol.
-					($this->hive['DEBUG']?('<pre>'.$trace.'</pre>'.$eol):'').
-				'</body>'.$eol.
-				'</html>');
-		if ($this->hive['HALT'])
-			die;
-	}
-
-	/**
-	*	Mock HTTP request
-	*	@return mixed
-	*	@param $pattern string
-	*	@param $args array
-	*	@param $headers array
-	*	@param $body string
-	**/
-	function mock($pattern,
-		array $args=NULL,array $headers=NULL,$body=NULL) {
-		if (!$args)
-			$args=array();
-		$types=array('sync','ajax');
-		preg_match('/([\|\w]+)\h+(?:@(\w+)(?:(\(.+?)\))*|([^\h]+))'.
-			'(?:\h+\[('.implode('|',$types).')\])?/',$pattern,$parts);
-		$verb=strtoupper($parts[1]);
-		if ($parts[2]) {
-			if (empty($this->hive['ALIASES'][$parts[2]]))
-				user_error(sprintf(self::E_Named,$parts[2]),E_USER_ERROR);
-			$parts[4]=$this->hive['ALIASES'][$parts[2]];
-			$parts[4]=$this->build($parts[4],
-				isset($parts[3])?$this->parse($parts[3]):array());
-		}
-		if (empty($parts[4]))
-			user_error(sprintf(self::E_Pattern,$pattern),E_USER_ERROR);
-		$url=parse_url($parts[4]);
-		parse_str(@$url['query'],$GLOBALS['_GET']);
-		if (preg_match('/GET|HEAD/',$verb))
-			$GLOBALS['_GET']=array_merge($GLOBALS['_GET'],$args);
-		$GLOBALS['_POST']=$verb=='POST'?$args:array();
-		$GLOBALS['_REQUEST']=array_merge($GLOBALS['_GET'],$GLOBALS['_POST']);
-		foreach ($headers?:array() as $key=>$val)
-			$_SERVER['HTTP_'.strtr(strtoupper($key),'-','_')]=$val;
-		$this->hive['VERB']=$verb;
-		$this->hive['URI']=$this->hive['BASE'].$url['path'];
-		if ($GLOBALS['_GET'])
-			$this->hive['URI'].='?'.http_build_query($GLOBALS['_GET']);
-		$this->hive['BODY']='';
-		if (!preg_match('/GET|HEAD/',$verb))
-			$this->hive['BODY']=$body?:http_build_query($args);
-		$this->hive['AJAX']=isset($parts[5]) &&
-			preg_match('/ajax/i',$parts[5]);
-		return $this->run();
-	}
-
-	/**
-	*	Bind handler to route pattern
-	*	@return NULL
-	*	@param $pattern string|array
-	*	@param $handler callback
-	*	@param $ttl int
-	*	@param $kbps int
-	**/
-	function route($pattern,$handler,$ttl=0,$kbps=0) {
-		$types=array('sync','ajax');
-		$alias=null;
-		if (is_array($pattern)) {
-			foreach ($pattern as $item)
-				$this->route($item,$handler,$ttl,$kbps);
-			return;
-		}
-		preg_match('/([\|\w]+)\h+(?:(?:@(\w+)\h*:\h*)?(@(\w+)|[^\h]+))'.
-			'(?:\h+\[('.implode('|',$types).')\])?/',$pattern,$parts);
-		if (isset($parts[2]) && $parts[2])
-			$this->hive['ALIASES'][$alias=$parts[2]]=$parts[3];
-		elseif (!empty($parts[4])) {
-			if (empty($this->hive['ALIASES'][$parts[4]]))
-				user_error(sprintf(self::E_Named,$parts[4]),E_USER_ERROR);
-			$parts[3]=$this->hive['ALIASES'][$alias=$parts[4]];
-		}
-		if (empty($parts[3]))
-			user_error(sprintf(self::E_Pattern,$pattern),E_USER_ERROR);
-		$type=empty($parts[5])?
-			self::REQ_SYNC|self::REQ_AJAX:
-			constant('self::REQ_'.strtoupper($parts[5]));
-		foreach ($this->split($parts[1]) as $verb) {
-			if (!preg_match('/'.self::VERBS.'/',$verb))
-				$this->error(501,$verb.' '.$this->hive['URI']);
-			$this->hive['ROUTES'][$parts[3]][$type][strtoupper($verb)]=
-				array($handler,$ttl,$kbps,$alias);
-		}
-	}
-
-	/**
-	*	Reroute to specified URI
-	*	@return NULL
-	*	@param $url string
-	*	@param $permanent bool
-	**/
-	function reroute($url=NULL,$permanent=FALSE) {
-		if (!$url)
-			$url=$this->hive['REALM'];
-		if (preg_match('/^(?:@(\w+)(?:(\(.+?)\))*)/',$url,$parts)) {
-			if (empty($this->hive['ALIASES'][$parts[1]]))
-				user_error(sprintf(self::E_Named,$parts[1]),E_USER_ERROR);
-			$url=$this->hive['ALIASES'][$parts[1]];
-		}
-		$url=$this->build($url,
-			isset($parts[2])?$this->parse($parts[2]):array());
-		if (($handler=$this->hive['ONREROUTE']) &&
-			$this->call($handler,array($url,$permanent))!==FALSE)
-			return;
-		if ($url[0]=='/')
-			$url=$this->hive['BASE'].$url;
-		if (PHP_SAPI!='cli') {
-			header('Location: '.$url);
-			$this->status($permanent?301:302);
-			die;
-		}
-		$this->mock('GET '.$url);
-	}
-
-	/**
-	*	Provide ReST interface by mapping HTTP verb to class method
-	*	@return NULL
-	*	@param $url string
-	*	@param $class string|object
-	*	@param $ttl int
-	*	@param $kbps int
-	**/
-	function map($url,$class,$ttl=0,$kbps=0) {
-		if (is_array($url)) {
-			foreach ($url as $item)
-				$this->map($item,$class,$ttl,$kbps);
-			return;
-		}
-		foreach (explode('|',self::VERBS) as $method)
-			$this->route($method.' '.$url,is_string($class)?
-				$class.'->'.$this->hive['PREMAP'].strtolower($method):
-				array($class,$this->hive['PREMAP'].strtolower($method)),
-				$ttl,$kbps);
-	}
-
-	/**
-	*	Redirect a route to another URL
-	*	@return NULL
-	*	@param $pattern string|array
-	*	@param $url string
-	*	@param $permanent bool
-	*/
-	function redirect($pattern,$url,$permanent=TRUE) {
-		if (is_array($pattern)) {
-			foreach ($pattern as $item)
-				$this->redirect($item,$url,$permanent);
-			return;
-		}
-		$this->route($pattern,function($fw) use($url,$permanent) {
-			$fw->reroute($url,$permanent);
-		});
-	}
-
-	/**
-	*	Return TRUE if IPv4 address exists in DNSBL
-	*	@return bool
-	*	@param $ip string
-	**/
-	function blacklisted($ip) {
-		if ($this->hive['DNSBL'] &&
-			!in_array($ip,
-				is_array($this->hive['EXEMPT'])?
-					$this->hive['EXEMPT']:
-					$this->split($this->hive['EXEMPT']))) {
-			// Reverse IPv4 dotted quad
-			$rev=implode('.',array_reverse(explode('.',$ip)));
-			foreach (is_array($this->hive['DNSBL'])?
-				$this->hive['DNSBL']:
-				$this->split($this->hive['DNSBL']) as $server)
-				// DNSBL lookup
-				if (checkdnsrr($rev.'.'.$server,'A'))
-					return TRUE;
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Applies the specified URL mask and returns parameterized matches
-	*	@return $args array
-	*	@param $pattern string
-	*	@param $url string|NULL
-	**/
-	function mask($pattern,$url=NULL) {
-		if (!$url)
-			$url=$this->rel($this->hive['URI']);
-		$case=$this->hive['CASELESS']?'i':'';
-		preg_match('/^'.
-			preg_replace('/@(\w+\b)/','(?P<\1>[^\/\?]+)',
-			str_replace('\*','([^\?]+)',preg_quote($pattern,'/'))).
-				'\/?(?:\?.*)?$/'.$case.'um',$url,$args);
-		return $args;
-	}
-
-	/**
-	*	Match routes against incoming URI
-	*	@return mixed
-	**/
-	function run() {
-		if ($this->blacklisted($this->hive['IP']))
-			// Spammer detected
-			$this->error(403);
-		if (!$this->hive['ROUTES'])
-			// No routes defined
-			user_error(self::E_Routes,E_USER_ERROR);
-		// Match specific routes first
-		$paths=array();
-		foreach ($keys=array_keys($this->hive['ROUTES']) as $key)
-			$paths[]=str_replace('@','*@',$key);
-		$vals=array_values($this->hive['ROUTES']);
-		array_multisort($paths,SORT_DESC,$keys,$vals);
-		$this->hive['ROUTES']=array_combine($keys,$vals);
-		// Convert to BASE-relative URL
-		$req=$this->rel($this->hive['URI']);
-		if ($cors=(isset($this->hive['HEADERS']['Origin']) &&
-			$this->hive['CORS']['origin'])) {
-			$cors=$this->hive['CORS'];
-			header('Access-Control-Allow-Origin: '.$cors['origin']);
-			header('Access-Control-Allow-Credentials: '.
-				($cors['credentials']?'true':'false'));
-		}
-		$allowed=array();
-		foreach ($this->hive['ROUTES'] as $pattern=>$routes) {
-			if (!$args=$this->mask($pattern,$req))
-				continue;
-			ksort($args);
-			$route=NULL;
-			if (isset(
-				$routes[$ptr=$this->hive['AJAX']+1][$this->hive['VERB']]))
-				$route=$routes[$ptr];
-			elseif (isset($routes[self::REQ_SYNC|self::REQ_AJAX]))
-				$route=$routes[self::REQ_SYNC|self::REQ_AJAX];
-			if (!$route)
-				continue;
-			if ($this->hive['VERB']!='OPTIONS' &&
-				isset($route[$this->hive['VERB']])) {
-				$parts=parse_url($req);
-				if ($this->hive['VERB']=='GET' &&
-					preg_match('/.+\/$/',$parts['path']))
-					$this->reroute(substr($parts['path'],0,-1).
-						(isset($parts['query'])?('?'.$parts['query']):''));
-				list($handler,$ttl,$kbps,$alias)=$route[$this->hive['VERB']];
-				if (is_bool(strpos($pattern,'/*')))
-					foreach (array_keys($args) as $key)
-						if (is_numeric($key) && $key)
-							unset($args[$key]);
-				// Capture values of route pattern tokens
-				$this->hive['PARAMS']=$args=array_map('urldecode',$args);
-				// Save matching route
-				$this->hive['ALIAS']=$alias;
-				$this->hive['PATTERN']=$pattern;
-				if ($cors && $cors['expose'])
-					header('Access-Control-Expose-Headers: '.(is_array($cors['expose'])?
-						implode(',',$cors['expose']):$cors['expose']));
-				if (is_string($handler)) {
-					// Replace route pattern tokens in handler if any
-					$handler=preg_replace_callback('/@(\w+\b)/',
-						function($id) use($args) {
-							return isset($args[$id[1]])?$args[$id[1]]:$id[0];
-						},
-						$handler
-					);
-					if (preg_match('/(.+)\h*(?:->|::)/',$handler,$match) &&
-						!class_exists($match[1]))
-						$this->error(404);
-				}
-				// Process request
-				$result=NULL;
-				$body='';
-				$now=microtime(TRUE);
-				if (preg_match('/GET|HEAD/',$this->hive['VERB']) && $ttl) {
-					// Only GET and HEAD requests are cacheable
-					$headers=$this->hive['HEADERS'];
-					$cache=Cache::instance();
-					$cached=$cache->exists(
-						$hash=$this->hash($this->hive['VERB'].' '.
-							$this->hive['URI']).'.url',$data);
-					if ($cached && $cached[0]+$ttl>$now) {
-						if (isset($headers['If-Modified-Since']) &&
-							strtotime($headers['If-Modified-Since'])+
-								$ttl>$now) {
-							$this->status(304);
-							die;
-						}
-						// Retrieve from cache backend
-						list($headers,$body,$result)=$data;
-						if (PHP_SAPI!='cli')
-							array_walk($headers,'header');
-						$this->expire($cached[0]+$ttl-$now);
-					}
-					else
-						// Expire HTTP client-cached page
-						$this->expire($ttl);
-				}
-				else
-					$this->expire(0);
-				if (!strlen($body)) {
-					if (!$this->hive['RAW'] && !$this->hive['BODY'])
-						$this->hive['BODY']=file_get_contents('php://input');
-					ob_start();
-					// Call route handler
-					$result=$this->call($handler,array($this,$args),
-						'beforeroute,afterroute');
-					$body=ob_get_clean();
-					if (isset($cache) && !error_get_last()) {
-						// Save to cache backend
-						$cache->set($hash,array(
-							// Remove cookies
-							preg_grep('/Set-Cookie\:/',headers_list(),
-								PREG_GREP_INVERT),$body,$result),$ttl);
-					}
-				}
-				$this->hive['RESPONSE']=$body;
-				if (!$this->hive['QUIET']) {
-					if ($kbps) {
-						$ctr=0;
-						foreach (str_split($body,1024) as $part) {
-							// Throttle output
-							$ctr++;
-							if ($ctr/$kbps>($elapsed=microtime(TRUE)-$now) &&
-								!connection_aborted())
-								usleep(1e6*($ctr/$kbps-$elapsed));
-							echo $part;
-						}
-					}
-					else
-						echo $body;
-				}
-				return $result;
-			}
-			$allowed=array_merge($allowed,array_keys($route));
-		}
-		if (!$allowed)
-			// URL doesn't match any route
-			$this->error(404);
-		elseif (PHP_SAPI!='cli') {
-			// Unhandled HTTP method
-			header('Allow: '.implode(',',array_unique($allowed)));
-			if ($cors) {
-				header('Access-Control-Allow-Methods: OPTIONS,'.implode(',',$allowed));
-				if ($cors['headers'])
-					header('Access-Control-Allow-Headers: '.(is_array($cors['headers'])?
-						implode(',',$cors['headers']):$cors['headers']));
-				if ($cors['ttl']>0)
-					header('Access-Control-Max-Age: '.$cors['ttl']);
-			}
-			if ($this->hive['VERB']!='OPTIONS')
-				$this->error(405);
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Loop until callback returns TRUE (for long polling)
-	*	@return mixed
-	*	@param $func callback
-	*	@param $args array
-	*	@param $timeout int
-	**/
-	function until($func,$args=NULL,$timeout=60) {
-		if (!$args)
-			$args=array();
-		$time=time();
-		$limit=max(0,min($timeout,$max=ini_get('max_execution_time')-1));
-		$out='';
-		$flag=FALSE;
-		// Not for the weak of heart
-		while (
-			// Still alive?
-			!connection_aborted() &&
-			// Got time left?
-			(time()-$time+1<$limit) &&
-			// Restart session
-			$flag=@session_start() &&
-			// CAUTION: Callback will kill host if it never becomes truthy!
-			!($out=$this->call($func,$args))) {
-			session_commit();
-			ob_flush();
-			flush();
-			// Hush down
-			sleep(1);
-		}
-		if ($flag) {
-			session_commit();
-			ob_flush();
-			flush();
-		}
-		return $out;
-	}
-
-	/**
-	*	Disconnect HTTP client
-	**/
-	function abort() {
-		@session_start();
-		session_commit();
-		header('Content-Length: 0');
-		while (ob_get_level())
-			ob_end_clean();
-		flush();
-		if (function_exists('fastcgi_finish_request'))
-			fastcgi_finish_request();
-	}
-
-	/**
-	*	Grab the real route handler behind the string expression
-	*	@return string|array
-	*	@param $func string
-	*	@param $args array
-	**/
-	function grab($func,$args=NULL) {
-		if (preg_match('/(.+)\h*(->|::)\h*(.+)/s',$func,$parts)) {
-			// Convert string to executable PHP callback
-			if (!class_exists($parts[1]))
-				user_error(sprintf(self::E_Class,$parts[1]),E_USER_ERROR);
-			if ($parts[2]=='->') {
-				if (is_subclass_of($parts[1],'Prefab'))
-					$parts[1]=call_user_func($parts[1].'::instance');
-				else {
-					$ref=new ReflectionClass($parts[1]);
-					$parts[1]=method_exists($parts[1],'__construct')?
-						$ref->newinstanceargs($args):
-						$ref->newinstance();
-				}
-			}
-			$func=array($parts[1],$parts[3]);
-		}
-		return $func;
-	}
-
-	/**
-	*	Execute callback/hooks (supports 'class->method' format)
-	*	@return mixed|FALSE
-	*	@param $func callback
-	*	@param $args mixed
-	*	@param $hooks string
-	**/
-	function call($func,$args=NULL,$hooks='') {
-		if (!is_array($args))
-			$args=array($args);
-		// Grab the real handler behind the string representation
-		if (is_string($func))
-			$func=$this->grab($func,$args);
-		// Execute function; abort if callback/hook returns FALSE
-		if (!is_callable($func))
-			// No route handler
-			if ($hooks=='beforeroute,afterroute') {
-				$allowed=array();
-				if (is_array($func))
-					$allowed=array_intersect(
-						array_map('strtoupper',get_class_methods($func[0])),
-						explode('|',self::VERBS)
-					);
-				header('Allow: '.implode(',',$allowed));
-				$this->error(405);
-			}
-			else
-				user_error(sprintf(self::E_Method,
-					is_string($func)?$func:$this->stringify($func)),
-					E_USER_ERROR);
-		$obj=FALSE;
-		if (is_array($func)) {
-			$hooks=$this->split($hooks);
-			$obj=TRUE;
-		}
-		// Execute pre-route hook if any
-		if ($obj && $hooks && in_array($hook='beforeroute',$hooks) &&
-			method_exists($func[0],$hook) &&
-			call_user_func_array(array($func[0],$hook),$args)===FALSE)
-			return FALSE;
-		// Execute callback
-		$out=call_user_func_array($func,$args?:array());
-		if ($out===FALSE)
-			return FALSE;
-		// Execute post-route hook if any
-		if ($obj && $hooks && in_array($hook='afterroute',$hooks) &&
-			method_exists($func[0],$hook) &&
-			call_user_func_array(array($func[0],$hook),$args)===FALSE)
-			return FALSE;
-		return $out;
-	}
-
-	/**
-	*	Execute specified callbacks in succession; Apply same arguments
-	*	to all callbacks
-	*	@return array
-	*	@param $funcs array|string
-	*	@param $args mixed
-	**/
-	function chain($funcs,$args=NULL) {
-		$out=array();
-		foreach (is_array($funcs)?$funcs:$this->split($funcs) as $func)
-			$out[]=$this->call($func,$args);
-		return $out;
-	}
-
-	/**
-	*	Execute specified callbacks in succession; Relay result of
-	*	previous callback as argument to the next callback
-	*	@return array
-	*	@param $funcs array|string
-	*	@param $args mixed
-	**/
-	function relay($funcs,$args=NULL) {
-		foreach (is_array($funcs)?$funcs:$this->split($funcs) as $func)
-			$args=array($this->call($func,$args));
-		return array_shift($args);
-	}
-
-	/**
-	*	Configure framework according to .ini-style file settings;
-	*	If optional 2nd arg is provided, template strings are interpreted
-	*	@return object
-	*	@param $file string
-	*	@param $allow bool
-	**/
-	function config($file,$allow=FALSE) {
-		preg_match_all(
-			'/(?<=^|\n)(?:'.
-				'\[(?<section>.+?)\]|'.
-				'(?<lval>[^\h\r\n;].*?)\h*=\h*'.
-				'(?<rval>(?:\\\\\h*\r?\n|.+?)*)'.
-			')(?=\r?\n|$)/',
-			$this->read($file),
-			$matches,PREG_SET_ORDER);
-		if ($matches) {
-			$sec='globals';
-			foreach ($matches as $match) {
-				if ($match['section']) {
-					$sec=$match['section'];
-					if (preg_match('/^(?!(?:global|config|route|map|redirect)s\b)'.
-						'((?:\.?\w)+)/i',$sec,$msec) && !$this->exists($msec[0]))
-						$this->set($msec[0],NULL);
-				}
-				else {
-					if ($allow) {
-						$match['lval']=Preview::instance()->
-							resolve($match['lval']);
-						$match['rval']=Preview::instance()->
-							resolve($match['rval']);
-					}
-					if (preg_match('/^(config|route|map|redirect)s\b/i',
-						$sec,$cmd)) {
-						call_user_func_array(
-							array($this,$cmd[1]),
-							array_merge(array($match['lval']),
-								str_getcsv($match['rval'])));
-					}
-					else {
-						$args=array_map(
-							function($val) {
-								if (is_numeric($val))
-									return $val+0;
-								$val=ltrim($val);
-								if (preg_match('/^\w+$/i',$val) &&
-									defined($val))
-									return constant($val);
-								return trim(preg_replace(
-									array('/\\\\"/','/\\\\\h*(\r?\n)/'),
-									array('"','\1'),$val));
-							},
-							// Mark quoted strings with 0x00 whitespace
-							str_getcsv(preg_replace('/(?<!\\\\)(")(.*?)\1/',
-								"\\1\x00\\2\\1",$match['rval']))
-						);
-						preg_match('/^(?<section>[^:]+)(?:\:(?<func>.+))?/',
-							$sec,$parts);
-						$func=isset($parts['func'])?$parts['func']:NULL;
-						$custom=(strtolower($parts['section'])!='globals');
-						if ($func)
-							$args=array($this->call($func,
-								count($args)>1?array($args):$args));
-						call_user_func_array(
-							array($this,'set'),
-							array_merge(
-								array(
-									($custom?($parts['section'].'.'):'').
-									$match['lval']
-								),
-								count($args)>1?array($args):$args
-							)
-						);
-					}
-				}
-			}
-		}
-		return $this;
-	}
-
-	/**
-	*	Create mutex, invoke callback then drop ownership when done
-	*	@return mixed
-	*	@param $id string
-	*	@param $func callback
-	*	@param $args mixed
-	**/
-	function mutex($id,$func,$args=NULL) {
-		if (!is_dir($tmp=$this->hive['TEMP']))
-			mkdir($tmp,self::MODE,TRUE);
-		// Use filesystem lock
-		if (is_file($lock=$tmp.
-			$this->hash($this->hive['ROOT'].$this->hive['BASE']).'.'.
-			$this->hash($id).'.lock') &&
-			filemtime($lock)+ini_get('max_execution_time')<microtime(TRUE))
-			// Stale lock
-			@unlink($lock);
-		while (!($handle=@fopen($lock,'x')) && !connection_aborted())
-			usleep(mt_rand(0,100));
-		$out=$this->call($func,$args);
-		fclose($handle);
-		@unlink($lock);
-		return $out;
-	}
-
-	/**
-	*	Read file (with option to apply Unix LF as standard line ending)
-	*	@return string
-	*	@param $file string
-	*	@param $lf bool
-	**/
-	function read($file,$lf=FALSE) {
-		$out=@file_get_contents($file);
-		return $lf?preg_replace('/\r\n|\r/',"\n",$out):$out;
-	}
-
-	/**
-	*	Exclusive file write
-	*	@return int|FALSE
-	*	@param $file string
-	*	@param $data mixed
-	*	@param $append bool
-	**/
-	function write($file,$data,$append=FALSE) {
-		return file_put_contents($file,$data,LOCK_EX|($append?FILE_APPEND:0));
-	}
-
-	/**
-	*	Apply syntax highlighting
-	*	@return string
-	*	@param $text string
-	**/
-	function highlight($text) {
-		$out='';
-		$pre=FALSE;
-		$text=trim($text);
-		if (!preg_match('/^<\?php/',$text)) {
-			$text='<?php '.$text;
-			$pre=TRUE;
-		}
-		foreach (token_get_all($text) as $token)
-			if ($pre)
-				$pre=FALSE;
-			else
-				$out.='<span'.
-					(is_array($token)?
-						(' class="'.
-							substr(strtolower(token_name($token[0])),2).'">'.
-							$this->encode($token[1]).''):
-						('>'.$this->encode($token))).
-					'</span>';
-		return $out?('<code>'.$out.'</code>'):$text;
-	}
-
-	/**
-	*	Dump expression with syntax highlighting
-	*	@return NULL
-	*	@param $expr mixed
-	**/
-	function dump($expr) {
-		echo $this->highlight($this->stringify($expr));
-	}
-
-	/**
-	*	Return path (and query parameters) relative to the base directory
-	*	@return string
-	*	@param $url string
-	**/
-	function rel($url) {
-		return preg_replace('/^(?:https?:\/\/)?'.
-			preg_quote($this->hive['BASE'],'/').'(\/.*|$)/','\1',$url);
-	}
-
-	/**
-	*	Namespace-aware class autoloader
-	*	@return mixed
-	*	@param $class string
-	**/
-	protected function autoload($class) {
-		$class=$this->fixslashes(ltrim($class,'\\'));
-		$func=NULL;
-		if (is_array($path=$this->hive['AUTOLOAD']) &&
-			isset($path[1]) && is_callable($path[1]))
-			list($path,$func)=$path;
-		foreach ($this->split($this->hive['PLUGINS'].';'.$path) as $auto)
-			if ($func && is_file($file=$func($auto.$class).'.php') ||
-				is_file($file=$auto.$class.'.php') ||
-				is_file($file=$auto.strtolower($class).'.php') ||
-				is_file($file=strtolower($auto.$class).'.php'))
-				return require($file);
-	}
-
-	/**
-	*	Execute framework/application shutdown sequence
-	*	@return NULL
-	*	@param $cwd string
-	**/
-	function unload($cwd) {
-		chdir($cwd);
-		if (!$error=error_get_last())
-			@session_commit();
-		$handler=$this->hive['UNLOAD'];
-		if ((!$handler || $this->call($handler,$this)===FALSE) &&
-			$error && in_array($error['type'],
-			array(E_ERROR,E_PARSE,E_CORE_ERROR,E_COMPILE_ERROR)))
-			// Fatal error detected
-			$this->error(500,sprintf(self::E_Fatal,$error['message']),
-				array($error));
-	}
-
-	/**
-	*	Convenience method for checking hive key
-	*	@return mixed
-	*	@param $key string
-	**/
-	function offsetexists($key) {
-		return $this->exists($key);
-	}
-
-	/**
-	*	Convenience method for assigning hive value
-	*	@return mixed
-	*	@param $key string
-	*	@param $val scalar
-	**/
-	function offsetset($key,$val) {
-		return $this->set($key,$val);
-	}
-
-	/**
-	*	Convenience method for retrieving hive value
-	*	@return mixed
-	*	@param $key string
-	**/
-	function &offsetget($key) {
-		$val=&$this->ref($key);
-		return $val;
-	}
-
-	/**
-	*	Convenience method for removing hive key
-	*	@return NULL
-	*	@param $key string
-	**/
-	function offsetunset($key) {
-		$this->clear($key);
-	}
-
-	/**
-	*	Alias for offsetexists()
-	*	@return mixed
-	*	@param $key string
-	**/
-	function __isset($key) {
-		return $this->offsetexists($key);
-	}
-
-	/**
-	*	Alias for offsetset()
-	*	@return mixed
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	function __set($key,$val) {
-		return $this->offsetset($key,$val);
-	}
-
-	/**
-	*	Alias for offsetget()
-	*	@return mixed
-	*	@param $key string
-	**/
-	function &__get($key) {
-		$val=&$this->offsetget($key);
-		return $val;
-	}
-
-	/**
-	*	Alias for offsetunset()
-	*	@return mixed
-	*	@param $key string
-	**/
-	function __unset($key) {
-		$this->offsetunset($key);
-	}
-
-	/**
-	*	Call function identified by hive key
-	*	@return mixed
-	*	@param $key string
-	*	@param $args array
-	**/
-	function __call($key,$args) {
-		return call_user_func_array($this->get($key),$args);
-	}
-
-	//! Prohibit cloning
-	private function __clone() {
-	}
-
-	//! Bootstrap
-	function __construct() {
-		// Managed directives
-		ini_set('default_charset',$charset='UTF-8');
-		if (extension_loaded('mbstring'))
-			mb_internal_encoding($charset);
-		ini_set('display_errors',0);
-		// Deprecated directives
-		@ini_set('magic_quotes_gpc',0);
-		@ini_set('register_globals',0);
-		// Intercept errors/exceptions; PHP5.3-compatible
-		error_reporting((E_ALL|E_STRICT)&~(E_NOTICE|E_USER_NOTICE));
-		$fw=$this;
-		set_exception_handler(
-			function($obj) use($fw) {
-				$fw->hive['EXCEPTION']=$obj;
-				$fw->error(500,$obj->getmessage(),$obj->gettrace());
-			}
-		);
-		set_error_handler(
-			function($code,$text) use($fw) {
-				if ($code & error_reporting())
-					$fw->error(500,$text);
-			}
-		);
-		if (!isset($_SERVER['SERVER_NAME']))
-			$_SERVER['SERVER_NAME']=gethostname();
-		if (PHP_SAPI=='cli') {
-			// Emulate HTTP request
-			if (isset($_SERVER['argc']) && $_SERVER['argc']<2) {
-				$_SERVER['argc']++;
-				$_SERVER['argv'][1]='/';
-			}
-			$_SERVER['REQUEST_METHOD']='GET';
-			$_SERVER['REQUEST_URI']=$_SERVER['argv'][1];
-		}
-		$headers=array();
-		if (PHP_SAPI!='cli')
-			foreach (array_keys($_SERVER) as $key)
-				if (substr($key,0,5)=='HTTP_')
-					$headers[strtr(ucwords(strtolower(strtr(
-						substr($key,5),'_',' '))),' ','-')]=&$_SERVER[$key];
-		if (isset($headers['X-HTTP-Method-Override']))
-			$_SERVER['REQUEST_METHOD']=$headers['X-HTTP-Method-Override'];
-		elseif ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['_method']))
-			$_SERVER['REQUEST_METHOD']=$_POST['_method'];
-		$scheme=isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ||
-			isset($headers['X-Forwarded-Proto']) &&
-			$headers['X-Forwarded-Proto']=='https'?'https':'http';
-		// Create hive early on to expose header methods
-		$this->hive=array('HEADERS'=>$headers);
-		if (function_exists('apache_setenv')) {
-			// Work around Apache pre-2.4 VirtualDocumentRoot bug
-			$_SERVER['DOCUMENT_ROOT']=str_replace($_SERVER['SCRIPT_NAME'],'',
-				$_SERVER['SCRIPT_FILENAME']);
-			apache_setenv("DOCUMENT_ROOT",$_SERVER['DOCUMENT_ROOT']);
-		}
-		$_SERVER['DOCUMENT_ROOT']=realpath($_SERVER['DOCUMENT_ROOT']);
-		$base='';
-		if (PHP_SAPI!='cli')
-			$base=rtrim($this->fixslashes(
-				dirname($_SERVER['SCRIPT_NAME'])),'/');
-		$uri=parse_url($_SERVER['REQUEST_URI']);
-		$path=preg_replace('/^'.preg_quote($base,'/').'/','',$uri['path']);
-		call_user_func_array('session_set_cookie_params',
-			$jar=array(
-				'expire'=>0,
-				'path'=>$base?:'/',
-				'domain'=>is_int(strpos($_SERVER['SERVER_NAME'],'.')) &&
-					!filter_var($_SERVER['SERVER_NAME'],FILTER_VALIDATE_IP)?
-					$_SERVER['SERVER_NAME']:'',
-				'secure'=>($scheme=='https'),
-				'httponly'=>TRUE
-			)
-		);
-		$port=0;
-		if (isset($_SERVER['SERVER_PORT']))
-			$port=$_SERVER['SERVER_PORT'];
-		// Default configuration
-		$this->hive+=array(
-			'AGENT'=>$this->agent(),
-			'AJAX'=>$this->ajax(),
-			'ALIAS'=>NULL,
-			'ALIASES'=>array(),
-			'AUTOLOAD'=>'./',
-			'BASE'=>$base,
-			'BITMASK'=>ENT_COMPAT,
-			'BODY'=>NULL,
-			'CACHE'=>FALSE,
-			'CASELESS'=>TRUE,
-			'CONFIG'=>NULL,
-			'CORS'=>array(
-				'headers'=>'',
-				'origin'=>false,
-				'credentials'=>false,
-				'expose'=>false,
-				'ttl'=>0),
-			'DEBUG'=>0,
-			'DIACRITICS'=>array(),
-			'DNSBL'=>'',
-			'EMOJI'=>array(),
-			'ENCODING'=>$charset,
-			'ERROR'=>NULL,
-			'ESCAPE'=>TRUE,
-			'EXCEPTION'=>NULL,
-			'EXEMPT'=>NULL,
-			'FALLBACK'=>$this->fallback,
-			'FRAGMENT'=>isset($uri['fragment'])?$uri['fragment']:'',
-			'HALT'=>TRUE,
-			'HIGHLIGHT'=>TRUE,
-			'HOST'=>$_SERVER['SERVER_NAME'],
-			'IP'=>$this->ip(),
-			'JAR'=>$jar,
-			'LANGUAGE'=>isset($headers['Accept-Language'])?
-				$this->language($headers['Accept-Language']):
-				$this->fallback,
-			'LOCALES'=>'./',
-			'LOGS'=>'./',
-			'ONERROR'=>NULL,
-			'ONREROUTE'=>NULL,
-			'PACKAGE'=>self::PACKAGE,
-			'PARAMS'=>array(),
-			'PATH'=>$path,
-			'PATTERN'=>NULL,
-			'PLUGINS'=>$this->fixslashes(__DIR__).'/',
-			'PORT'=>$port,
-			'PREFIX'=>NULL,
-			'PREMAP'=>'',
-			'QUERY'=>isset($uri['query'])?$uri['query']:'',
-			'QUIET'=>FALSE,
-			'RAW'=>FALSE,
-			'REALM'=>$scheme.'://'.$_SERVER['SERVER_NAME'].
-				($port && $port!=80 && $port!=443?
-					(':'.$port):'').$_SERVER['REQUEST_URI'],
-			'RESPONSE'=>'',
-			'ROOT'=>$_SERVER['DOCUMENT_ROOT'],
-			'ROUTES'=>array(),
-			'SCHEME'=>$scheme,
-			'SERIALIZER'=>extension_loaded($ext='igbinary')?$ext:'php',
-			'TEMP'=>'tmp/',
-			'TIME'=>microtime(TRUE),
-			'TZ'=>(@ini_get('date.timezone'))?:'UTC',
-			'UI'=>'./',
-			'UNLOAD'=>NULL,
-			'UPLOADS'=>'./',
-			'URI'=>&$_SERVER['REQUEST_URI'],
-			'VERB'=>&$_SERVER['REQUEST_METHOD'],
-			'VERSION'=>self::VERSION,
-			'XFRAME'=>'SAMEORIGIN'
-		);
-		if (PHP_SAPI=='cli-server' &&
-			preg_match('/^'.preg_quote($base,'/').'$/',$this->hive['URI']))
-			$this->reroute('/');
-		if (ini_get('auto_globals_jit'))
-			// Override setting
-			$GLOBALS+=array('_ENV'=>$_ENV,'_REQUEST'=>$_REQUEST);
-		// Sync PHP globals with corresponding hive keys
-		$this->init=$this->hive;
-		foreach (explode('|',self::GLOBALS) as $global) {
-			$sync=$this->sync($global);
-			$this->init+=array(
-				$global=>preg_match('/SERVER|ENV/',$global)?$sync:array()
-			);
-		}
-		if ($error=error_get_last())
-			// Error detected
-			$this->error(500,sprintf(self::E_Fatal,$error['message']),
-				array($error));
-		date_default_timezone_set($this->hive['TZ']);
-		// Register framework autoloader
-		spl_autoload_register(array($this,'autoload'));
-		// Register shutdown handler
-		register_shutdown_function(array($this,'unload'),getcwd());
-	}
-
-}
-
-//! Cache engine
-class Cache extends Prefab {
-
-	protected
-		//! Cache DSN
-		$dsn,
-		//! Prefix for cache entries
-		$prefix,
-		//! MemCache or Redis object
-		$ref;
-
-	/**
-	*	Return timestamp and TTL of cache entry or FALSE if not found
-	*	@return array|FALSE
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	function exists($key,&$val=NULL) {
-		$fw=Base::instance();
-		if (!$this->dsn)
-			return FALSE;
-		$ndx=$this->prefix.'.'.$key;
-		$parts=explode('=',$this->dsn,2);
-		switch ($parts[0]) {
-			case 'apc':
-			case 'apcu':
-				$raw=apc_fetch($ndx);
-				break;
-			case 'redis':
-				$raw=$this->ref->get($ndx);
-				break;
-			case 'memcache':
-				$raw=memcache_get($this->ref,$ndx);
-				break;
-			case 'wincache':
-				$raw=wincache_ucache_get($ndx);
-				break;
-			case 'xcache':
-				$raw=xcache_get($ndx);
-				break;
-			case 'folder':
-				$raw=$fw->read($parts[1].$ndx);
-				break;
-		}
-		if (!empty($raw)) {
-			list($val,$time,$ttl)=(array)$fw->unserialize($raw);
-			if ($ttl===0 || $time+$ttl>microtime(TRUE))
-				return array($time,$ttl);
-			$val=null;
-			$this->clear($key);
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Store value in cache
-	*	@return mixed|FALSE
-	*	@param $key string
-	*	@param $val mixed
-	*	@param $ttl int
-	**/
-	function set($key,$val,$ttl=0) {
-		$fw=Base::instance();
-		if (!$this->dsn)
-			return TRUE;
-		$ndx=$this->prefix.'.'.$key;
-		$time=microtime(TRUE);
-		if ($cached=$this->exists($key))
-			list($time,$ttl)=$cached;
-		$data=$fw->serialize(array($val,$time,$ttl));
-		$parts=explode('=',$this->dsn,2);
-		switch ($parts[0]) {
-			case 'apc':
-			case 'apcu':
-				return apc_store($ndx,$data,$ttl);
-			case 'redis':
-				return $this->ref->set($ndx,$data,array('ex'=>$ttl));
-			case 'memcache':
-				return memcache_set($this->ref,$ndx,$data,0,$ttl);
-			case 'wincache':
-				return wincache_ucache_set($ndx,$data,$ttl);
-			case 'xcache':
-				return xcache_set($ndx,$data,$ttl);
-			case 'folder':
-				return $fw->write($parts[1].$ndx,$data);
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Retrieve value of cache entry
-	*	@return mixed|FALSE
-	*	@param $key string
-	**/
-	function get($key) {
-		return $this->dsn && $this->exists($key,$data)?$data:FALSE;
-	}
-
-	/**
-	*	Delete cache entry
-	*	@return bool
-	*	@param $key string
-	**/
-	function clear($key) {
-		if (!$this->dsn)
-			return;
-		$ndx=$this->prefix.'.'.$key;
-		$parts=explode('=',$this->dsn,2);
-		switch ($parts[0]) {
-			case 'apc':
-			case 'apcu':
-				return apc_delete($ndx);
-			case 'redis':
-				return $this->ref->del($ndx);
-			case 'memcache':
-				return memcache_delete($this->ref,$ndx);
-			case 'wincache':
-				return wincache_ucache_delete($ndx);
-			case 'xcache':
-				return xcache_unset($ndx);
-			case 'folder':
-				return @unlink($parts[1].$ndx);
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Clear contents of cache backend
-	*	@return bool
-	*	@param $suffix string
-	*	@param $lifetime int
-	**/
-	function reset($suffix=NULL,$lifetime=0) {
-		if (!$this->dsn)
-			return TRUE;
-		$regex='/'.preg_quote($this->prefix.'.','/').'.+?'.
-			preg_quote($suffix,'/').'/';
-		$parts=explode('=',$this->dsn,2);
-		switch ($parts[0]) {
-			case 'apc':
-			case 'apcu':
-				$info=apc_cache_info('user');
-				if (!empty($info['cache_list'])) {
-					$key=array_key_exists('info',$info['cache_list'][0])?'info':'key';
-					$mtkey=array_key_exists('mtime',$info['cache_list'][0])?
-						'mtime':'modification_time';
-					foreach ($info['cache_list'] as $item)
-						if (preg_match($regex,$item[$key]) &&
-							$item[$mtkey]+$lifetime<time())
-							apc_delete($item[$key]);
-				}
-				return TRUE;
-			case 'redis':
-				$fw=Base::instance();
-				$keys=$this->ref->keys($this->prefix.'.*'.$suffix);
-				foreach($keys as $key) {
-					$val=$fw->unserialize($this->ref->get($key));
-					if ($val[1]+$lifetime<time())
-						$this->ref->del($key);
-				}
-				return TRUE;
-			case 'memcache':
-				foreach (memcache_get_extended_stats(
-					$this->ref,'slabs') as $slabs)
-					foreach (array_filter(array_keys($slabs),'is_numeric')
-						as $id)
-						foreach (memcache_get_extended_stats(
-							$this->ref,'cachedump',$id) as $data)
-							if (is_array($data))
-								foreach ($data as $key=>$val)
-									if (preg_match($regex,$key) &&
-										$val[1]+$lifetime<time())
-										memcache_delete($this->ref,$key);
-				return TRUE;
-			case 'wincache':
-				$info=wincache_ucache_info();
-				foreach ($info['ucache_entries'] as $item)
-					if (preg_match($regex,$item['key_name']) &&
-						$item['use_time']+$lifetime<time())
-					wincache_ucache_delete($item['key_name']);
-				return TRUE;
-			case 'xcache':
-				return TRUE; /* Not supported */
-			case 'folder':
-				if ($glob=@glob($parts[1].'*'))
-					foreach ($glob as $file)
-						if (preg_match($regex,basename($file)) &&
-							filemtime($file)+$lifetime<time())
-							@unlink($file);
-				return TRUE;
-		}
-		return FALSE;
-	}
-
-	/**
-	*	Load/auto-detect cache backend
-	*	@return string
-	*	@param $dsn bool|string
-	**/
-	function load($dsn) {
-		$fw=Base::instance();
-		if ($dsn=trim($dsn)) {
-			if (preg_match('/^redis=(.+)/',$dsn,$parts) &&
-				extension_loaded('redis')) {
-				$port=6379;
-				$parts=explode(':',$parts[1],2);
-				if (count($parts)>1)
-					list($host,$port)=$parts;
-				else
-					$host=$parts[0];
-				$this->ref=new Redis;
-				if(!$this->ref->connect($host,$port,2))
-					$this->ref=NULL;
-			}
-			elseif (preg_match('/^memcache=(.+)/',$dsn,$parts) &&
-				extension_loaded('memcache'))
-				foreach ($fw->split($parts[1]) as $server) {
-					$port=11211;
-					$parts=explode(':',$server,2);
-					if (count($parts)>1)
-						list($host,$port)=$parts;
-					else
-						$host=$parts[0];
-					if (empty($this->ref))
-						$this->ref=@memcache_connect($host,$port)?:NULL;
-					else
-						memcache_add_server($this->ref,$host,$port);
-				}
-			if (empty($this->ref) && !preg_match('/^folder\h*=/',$dsn))
-				$dsn=($grep=preg_grep('/^(apc|wincache|xcache)/',
-					array_map('strtolower',get_loaded_extensions())))?
-						// Auto-detect
-						current($grep):
-						// Use filesystem as fallback
-						('folder='.$fw->get('TEMP').'cache/');
-			if (preg_match('/^folder\h*=\h*(.+)/',$dsn,$parts) &&
-				!is_dir($parts[1]))
-				mkdir($parts[1],Base::MODE,TRUE);
-		}
-		$this->prefix=$fw->hash($_SERVER['SERVER_NAME'].$fw->get('BASE'));
-		return $this->dsn=$dsn;
-	}
-
-	/**
-	*	Class constructor
-	*	@return object
-	*	@param $dsn bool|string
-	**/
-	function __construct($dsn=FALSE) {
-		if ($dsn)
-			$this->load($dsn);
-	}
-
-}
-
-//! View handler
-class View extends Prefab {
-
-	protected
-		//! Template file
-		$view,
-		//! post-rendering handler
-		$trigger,
-		//! Nesting level
-		$level=0;
-
-	/**
-	*	Encode characters to equivalent HTML entities
-	*	@return string
-	*	@param $arg mixed
-	**/
-	function esc($arg) {
-		$fw=Base::instance();
-		return $fw->recursive($arg,
-			function($val) use($fw) {
-				return is_string($val)?$fw->encode($val):$val;
-			}
-		);
-	}
-
-	/**
-	*	Decode HTML entities to equivalent characters
-	*	@return string
-	*	@param $arg mixed
-	**/
-	function raw($arg) {
-		$fw=Base::instance();
-		return $fw->recursive($arg,
-			function($val) use($fw) {
-				return is_string($val)?$fw->decode($val):$val;
-			}
-		);
-	}
-
-	/**
-	*	Create sandbox for template execution
-	*	@return string
-	*	@param $hive array
-	**/
-	protected function sandbox(array $hive=NULL) {
-		$this->level++;
-		$fw=Base::instance();
-		$implicit=false;
-		if ($hive === null) {
-			$implicit=true;
-			$hive=$fw->hive();
-		}
-		if ($this->level<2 || $implicit) {
-			if ($fw->get('ESCAPE'))
-				$hive=$this->esc($hive);
-			if (isset($hive['ALIASES']))
-				$hive['ALIASES']=$fw->build($hive['ALIASES']);
-		}
-		unset($fw, $implicit);
-		extract($hive);
-		unset($hive);
-		ob_start();
-		require($this->view);
-		$this->level--;
-		return ob_get_clean();
-	}
-
-	/**
-	*	Render template
-	*	@return string
-	*	@param $file string
-	*	@param $mime string
-	*	@param $hive array
-	*	@param $ttl int
-	**/
-	function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
-		$fw=Base::instance();
-		$cache=Cache::instance();
-		$cached=$cache->exists($hash=$fw->hash($file),$data);
-		if ($cached && $cached[0]+$ttl>microtime(TRUE))
-			return $data;
-		foreach ($fw->split($fw->get('UI').';./') as $dir)
-			if (is_file($this->view=$fw->fixslashes($dir.$file))) {
-				if (isset($_COOKIE[session_name()]))
-					@session_start();
-				$fw->sync('SESSION');
-				if ($mime && PHP_SAPI!='cli' && !headers_sent())
-					header('Content-Type: '.$mime.'; '.
-						'charset='.$fw->get('ENCODING'));
-				$data=$this->sandbox($hive);
-				if(isset($this->trigger['afterrender']))
-					foreach($this->trigger['afterrender'] as $func)
-						$data=$fw->call($func,$data);
-				if ($ttl)
-					$cache->set($hash,$data);
-				return $data;
-			}
-		user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
-	}
-
-	/**
-	*	post rendering handler
-	*	@param $func callback
-	*/
-	function afterrender($func) {
-		$this->trigger['afterrender'][]=$func;
-	}
-
-}
-
-//! Lightweight template engine
-class Preview extends View {
-
-	protected
-		//! MIME type
-		$mime,
-		//! token filter
-		$filter = array(
-			'esc'=>'$this->esc',
-			'raw'=>'$this->raw',
-			'alias'=>'\Base::instance()->alias',
-			'format'=>'\Base::instance()->format'
-		);
-
-	/**
-	*	Convert token to variable
-	*	@return string
-	*	@param $str string
-	**/
-	function token($str) {
-		return trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
-			Base::instance()->compile($str)));
-	}
-
-	/**
-	*	register token filter
-	*	@param string $key
-	*	@param string $func
-	*	@return array
-	*/
-	function filter($key=NULL,$func=NULL) {
-		if (!$key)
-			return array_keys($this->filter);
-		if (!$func)
-			return $this->filter[$key];
-		$this->filter[$key]=$func;
-	}
-
-	/**
-	*	Assemble markup
-	*	@return string
-	*	@param $node string
-	**/
-	protected function build($node) {
-		$self=$this;
-		return preg_replace_callback(
-			'/\{\-(.+?)\-\}|\{\{(.+?)\}\}(\n+)?/s',
-			function($expr) use($self) {
-				if ($expr[1])
-					return $expr[1];
-				$str=trim($self->token($expr[2]));
-				if (preg_match('/^([^|]+?)\h*\|(\h*\w+(?:\h*[,;]\h*\w+)*)/',
-					$str,$parts)) {
-					$str=$parts[1];
-					foreach (Base::instance()->split($parts[2]) as $func)
-						$str=$self->filter($func).'('.$str.')';
-				}
-				return '<?php echo '.$str.'; ?>'.
-					(isset($expr[3])?$expr[3]."\n":'');
-			},
-			preg_replace_callback(
-				'/\{~(.+?)~\}/s',
-				function($expr) use($self) {
-					return '<?php '.$self->token($expr[1]).' ?>';
-				},
-				$node
-			)
-		);
-	}
-
-	/**
-	*	Render template string
-	*	@return string
-	*	@param $str string
-	*	@param $hive array
-	**/
-	function resolve($str,array $hive=NULL) {
-		if (!$hive)
-			$hive=\Base::instance()->hive();
-		extract($hive);
-		ob_start();
-		eval(' ?>'.$this->build($str).'<?php ');
-		return ob_get_clean();
-	}
-
-	/**
-	*	Render template
-	*	@return string
-	*	@param $file string
-	*	@param $mime string
-	*	@param $hive array
-	*	@param $ttl int
-	**/
-	function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
-		$fw=Base::instance();
-		$cache=Cache::instance();
-		if (!is_dir($tmp=$fw->get('TEMP')))
-			mkdir($tmp,Base::MODE,TRUE);
-		foreach ($fw->split($fw->get('UI')) as $dir) {
-			$cached=$cache->exists($hash=$fw->hash($dir.$file),$data);
-			if ($cached && $cached[0]+$ttl>microtime(TRUE))
-				return $data;
-			if (is_file($view=$fw->fixslashes($dir.$file))) {
-				if (!is_file($this->view=($tmp.
-					$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
-					$fw->hash($view).'.php')) ||
-					filemtime($this->view)<filemtime($view)) {
-					// Remove PHP code and comments
-					$text=preg_replace(
-						'/(?<!["\'])\h*<\?(?:php|\s*=).+?\?>\h*'.
-						'(?!["\'])|\{\*.+?\*\}/is','',
-						$fw->read($view));
-					if (method_exists($this,'parse'))
-						$text=$this->parse($text);
-					$fw->write($this->view,$this->build($text));
-				}
-				if (isset($_COOKIE[session_name()]))
-					@session_start();
-				$fw->sync('SESSION');
-				if ($mime && PHP_SAPI!='cli' && !headers_sent())
-					header('Content-Type: '.($this->mime=$mime).'; '.
-						'charset='.$fw->get('ENCODING'));
-				$data=$this->sandbox($hive);
-				if(isset($this->trigger['afterrender']))
-					foreach ($this->trigger['afterrender'] as $func)
-						$data = $fw->call($func, $data);
-				if ($ttl)
-					$cache->set($hash,$data);
-				return $data;
-			}
-		}
-		user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
-	}
-
-}
-
-//! ISO language/country codes
-class ISO extends Prefab {
-
-	//@{ ISO 3166-1 country codes
-	const
-		CC_af='Afghanistan',
-		CC_ax='Åland Islands',
-		CC_al='Albania',
-		CC_dz='Algeria',
-		CC_as='American Samoa',
-		CC_ad='Andorra',
-		CC_ao='Angola',
-		CC_ai='Anguilla',
-		CC_aq='Antarctica',
-		CC_ag='Antigua and Barbuda',
-		CC_ar='Argentina',
-		CC_am='Armenia',
-		CC_aw='Aruba',
-		CC_au='Australia',
-		CC_at='Austria',
-		CC_az='Azerbaijan',
-		CC_bs='Bahamas',
-		CC_bh='Bahrain',
-		CC_bd='Bangladesh',
-		CC_bb='Barbados',
-		CC_by='Belarus',
-		CC_be='Belgium',
-		CC_bz='Belize',
-		CC_bj='Benin',
-		CC_bm='Bermuda',
-		CC_bt='Bhutan',
-		CC_bo='Bolivia',
-		CC_bq='Bonaire, Sint Eustatius and Saba',
-		CC_ba='Bosnia and Herzegovina',
-		CC_bw='Botswana',
-		CC_bv='Bouvet Island',
-		CC_br='Brazil',
-		CC_io='British Indian Ocean Territory',
-		CC_bn='Brunei Darussalam',
-		CC_bg='Bulgaria',
-		CC_bf='Burkina Faso',
-		CC_bi='Burundi',
-		CC_kh='Cambodia',
-		CC_cm='Cameroon',
-		CC_ca='Canada',
-		CC_cv='Cape Verde',
-		CC_ky='Cayman Islands',
-		CC_cf='Central African Republic',
-		CC_td='Chad',
-		CC_cl='Chile',
-		CC_cn='China',
-		CC_cx='Christmas Island',
-		CC_cc='Cocos (Keeling) Islands',
-		CC_co='Colombia',
-		CC_km='Comoros',
-		CC_cg='Congo',
-		CC_cd='Congo, The Democratic Republic of',
-		CC_ck='Cook Islands',
-		CC_cr='Costa Rica',
-		CC_ci='Côte d\'ivoire',
-		CC_hr='Croatia',
-		CC_cu='Cuba',
-		CC_cw='Curaçao',
-		CC_cy='Cyprus',
-		CC_cz='Czech Republic',
-		CC_dk='Denmark',
-		CC_dj='Djibouti',
-		CC_dm='Dominica',
-		CC_do='Dominican Republic',
-		CC_ec='Ecuador',
-		CC_eg='Egypt',
-		CC_sv='El Salvador',
-		CC_gq='Equatorial Guinea',
-		CC_er='Eritrea',
-		CC_ee='Estonia',
-		CC_et='Ethiopia',
-		CC_fk='Falkland Islands (Malvinas)',
-		CC_fo='Faroe Islands',
-		CC_fj='Fiji',
-		CC_fi='Finland',
-		CC_fr='France',
-		CC_gf='French Guiana',
-		CC_pf='French Polynesia',
-		CC_tf='French Southern Territories',
-		CC_ga='Gabon',
-		CC_gm='Gambia',
-		CC_ge='Georgia',
-		CC_de='Germany',
-		CC_gh='Ghana',
-		CC_gi='Gibraltar',
-		CC_gr='Greece',
-		CC_gl='Greenland',
-		CC_gd='Grenada',
-		CC_gp='Guadeloupe',
-		CC_gu='Guam',
-		CC_gt='Guatemala',
-		CC_gg='Guernsey',
-		CC_gn='Guinea',
-		CC_gw='Guinea-Bissau',
-		CC_gy='Guyana',
-		CC_ht='Haiti',
-		CC_hm='Heard Island and McDonald Islands',
-		CC_va='Holy See (Vatican City State)',
-		CC_hn='Honduras',
-		CC_hk='Hong Kong',
-		CC_hu='Hungary',
-		CC_is='Iceland',
-		CC_in='India',
-		CC_id='Indonesia',
-		CC_ir='Iran, Islamic Republic of',
-		CC_iq='Iraq',
-		CC_ie='Ireland',
-		CC_im='Isle of Man',
-		CC_il='Israel',
-		CC_it='Italy',
-		CC_jm='Jamaica',
-		CC_jp='Japan',
-		CC_je='Jersey',
-		CC_jo='Jordan',
-		CC_kz='Kazakhstan',
-		CC_ke='Kenya',
-		CC_ki='Kiribati',
-		CC_kp='Korea, Democratic People\'s Republic of',
-		CC_kr='Korea, Republic of',
-		CC_kw='Kuwait',
-		CC_kg='Kyrgyzstan',
-		CC_la='Lao People\'s Democratic Republic',
-		CC_lv='Latvia',
-		CC_lb='Lebanon',
-		CC_ls='Lesotho',
-		CC_lr='Liberia',
-		CC_ly='Libya',
-		CC_li='Liechtenstein',
-		CC_lt='Lithuania',
-		CC_lu='Luxembourg',
-		CC_mo='Macao',
-		CC_mk='Macedonia, The Former Yugoslav Republic of',
-		CC_mg='Madagascar',
-		CC_mw='Malawi',
-		CC_my='Malaysia',
-		CC_mv='Maldives',
-		CC_ml='Mali',
-		CC_mt='Malta',
-		CC_mh='Marshall Islands',
-		CC_mq='Martinique',
-		CC_mr='Mauritania',
-		CC_mu='Mauritius',
-		CC_yt='Mayotte',
-		CC_mx='Mexico',
-		CC_fm='Micronesia, Federated States of',
-		CC_md='Moldova, Republic of',
-		CC_mc='Monaco',
-		CC_mn='Mongolia',
-		CC_me='Montenegro',
-		CC_ms='Montserrat',
-		CC_ma='Morocco',
-		CC_mz='Mozambique',
-		CC_mm='Myanmar',
-		CC_na='Namibia',
-		CC_nr='Nauru',
-		CC_np='Nepal',
-		CC_nl='Netherlands',
-		CC_nc='New Caledonia',
-		CC_nz='New Zealand',
-		CC_ni='Nicaragua',
-		CC_ne='Niger',
-		CC_ng='Nigeria',
-		CC_nu='Niue',
-		CC_nf='Norfolk Island',
-		CC_mp='Northern Mariana Islands',
-		CC_no='Norway',
-		CC_om='Oman',
-		CC_pk='Pakistan',
-		CC_pw='Palau',
-		CC_ps='Palestinian Territory, Occupied',
-		CC_pa='Panama',
-		CC_pg='Papua New Guinea',
-		CC_py='Paraguay',
-		CC_pe='Peru',
-		CC_ph='Philippines',
-		CC_pn='Pitcairn',
-		CC_pl='Poland',
-		CC_pt='Portugal',
-		CC_pr='Puerto Rico',
-		CC_qa='Qatar',
-		CC_re='Réunion',
-		CC_ro='Romania',
-		CC_ru='Russian Federation',
-		CC_rw='Rwanda',
-		CC_bl='Saint Barthélemy',
-		CC_sh='Saint Helena, Ascension and Tristan da Cunha',
-		CC_kn='Saint Kitts and Nevis',
-		CC_lc='Saint Lucia',
-		CC_mf='Saint Martin (French Part)',
-		CC_pm='Saint Pierre and Miquelon',
-		CC_vc='Saint Vincent and The Grenadines',
-		CC_ws='Samoa',
-		CC_sm='San Marino',
-		CC_st='Sao Tome and Principe',
-		CC_sa='Saudi Arabia',
-		CC_sn='Senegal',
-		CC_rs='Serbia',
-		CC_sc='Seychelles',
-		CC_sl='Sierra Leone',
-		CC_sg='Singapore',
-		CC_sk='Slovakia',
-		CC_sx='Sint Maarten (Dutch Part)',
-		CC_si='Slovenia',
-		CC_sb='Solomon Islands',
-		CC_so='Somalia',
-		CC_za='South Africa',
-		CC_gs='South Georgia and The South Sandwich Islands',
-		CC_ss='South Sudan',
-		CC_es='Spain',
-		CC_lk='Sri Lanka',
-		CC_sd='Sudan',
-		CC_sr='Suriname',
-		CC_sj='Svalbard and Jan Mayen',
-		CC_sz='Swaziland',
-		CC_se='Sweden',
-		CC_ch='Switzerland',
-		CC_sy='Syrian Arab Republic',
-		CC_tw='Taiwan, Province of China',
-		CC_tj='Tajikistan',
-		CC_tz='Tanzania, United Republic of',
-		CC_th='Thailand',
-		CC_tl='Timor-Leste',
-		CC_tg='Togo',
-		CC_tk='Tokelau',
-		CC_to='Tonga',
-		CC_tt='Trinidad and Tobago',
-		CC_tn='Tunisia',
-		CC_tr='Turkey',
-		CC_tm='Turkmenistan',
-		CC_tc='Turks and Caicos Islands',
-		CC_tv='Tuvalu',
-		CC_ug='Uganda',
-		CC_ua='Ukraine',
-		CC_ae='United Arab Emirates',
-		CC_gb='United Kingdom',
-		CC_us='United States',
-		CC_um='United States Minor Outlying Islands',
-		CC_uy='Uruguay',
-		CC_uz='Uzbekistan',
-		CC_vu='Vanuatu',
-		CC_ve='Venezuela',
-		CC_vn='Viet Nam',
-		CC_vg='Virgin Islands, British',
-		CC_vi='Virgin Islands, U.S.',
-		CC_wf='Wallis and Futuna',
-		CC_eh='Western Sahara',
-		CC_ye='Yemen',
-		CC_zm='Zambia',
-		CC_zw='Zimbabwe';
-	//@}
-
-	//@{ ISO 639-1 language codes (Windows-compatibility subset)
-	const
-		LC_af='Afrikaans',
-		LC_am='Amharic',
-		LC_ar='Arabic',
-		LC_as='Assamese',
-		LC_ba='Bashkir',
-		LC_be='Belarusian',
-		LC_bg='Bulgarian',
-		LC_bn='Bengali',
-		LC_bo='Tibetan',
-		LC_br='Breton',
-		LC_ca='Catalan',
-		LC_co='Corsican',
-		LC_cs='Czech',
-		LC_cy='Welsh',
-		LC_da='Danish',
-		LC_de='German',
-		LC_dv='Divehi',
-		LC_el='Greek',
-		LC_en='English',
-		LC_es='Spanish',
-		LC_et='Estonian',
-		LC_eu='Basque',
-		LC_fa='Persian',
-		LC_fi='Finnish',
-		LC_fo='Faroese',
-		LC_fr='French',
-		LC_gd='Scottish Gaelic',
-		LC_gl='Galician',
-		LC_gu='Gujarati',
-		LC_he='Hebrew',
-		LC_hi='Hindi',
-		LC_hr='Croatian',
-		LC_hu='Hungarian',
-		LC_hy='Armenian',
-		LC_id='Indonesian',
-		LC_ig='Igbo',
-		LC_is='Icelandic',
-		LC_it='Italian',
-		LC_ja='Japanese',
-		LC_ka='Georgian',
-		LC_kk='Kazakh',
-		LC_km='Khmer',
-		LC_kn='Kannada',
-		LC_ko='Korean',
-		LC_lb='Luxembourgish',
-		LC_lo='Lao',
-		LC_lt='Lithuanian',
-		LC_lv='Latvian',
-		LC_mi='Maori',
-		LC_ml='Malayalam',
-		LC_mr='Marathi',
-		LC_ms='Malay',
-		LC_mt='Maltese',
-		LC_ne='Nepali',
-		LC_nl='Dutch',
-		LC_no='Norwegian',
-		LC_oc='Occitan',
-		LC_or='Oriya',
-		LC_pl='Polish',
-		LC_ps='Pashto',
-		LC_pt='Portuguese',
-		LC_qu='Quechua',
-		LC_ro='Romanian',
-		LC_ru='Russian',
-		LC_rw='Kinyarwanda',
-		LC_sa='Sanskrit',
-		LC_si='Sinhala',
-		LC_sk='Slovak',
-		LC_sl='Slovenian',
-		LC_sq='Albanian',
-		LC_sv='Swedish',
-		LC_ta='Tamil',
-		LC_te='Telugu',
-		LC_th='Thai',
-		LC_tk='Turkmen',
-		LC_tr='Turkish',
-		LC_tt='Tatar',
-		LC_uk='Ukrainian',
-		LC_ur='Urdu',
-		LC_vi='Vietnamese',
-		LC_wo='Wolof',
-		LC_yo='Yoruba',
-		LC_zh='Chinese';
-	//@}
-
-	/**
-	*	Return list of languages indexed by ISO 639-1 language code
-	*	@return array
-	**/
-	function languages() {
-		return \Base::instance()->constants($this,'LC_');
-	}
-
-	/**
-	*	Return list of countries indexed by ISO 3166-1 country code
-	*	@return array
-	**/
-	function countries() {
-		return \Base::instance()->constants($this,'CC_');
-	}
-
-}
-
-//! Container for singular object instances
-final class Registry {
-
-	private static
-		//! Object catalog
-		$table;
-
-	/**
-	*	Return TRUE if object exists in catalog
-	*	@return bool
-	*	@param $key string
-	**/
-	static function exists($key) {
-		return isset(self::$table[$key]);
-	}
-
-	/**
-	*	Add object to catalog
-	*	@return object
-	*	@param $key string
-	*	@param $obj object
-	**/
-	static function set($key,$obj) {
-		return self::$table[$key]=$obj;
-	}
-
-	/**
-	*	Retrieve object from catalog
-	*	@return object
-	*	@param $key string
-	**/
-	static function get($key) {
-		return self::$table[$key];
-	}
-
-	/**
-	*	Delete object from catalog
-	*	@return NULL
-	*	@param $key string
-	**/
-	static function clear($key) {
-		self::$table[$key]=NULL;
-		unset(self::$table[$key]);
-	}
-
-	//! Prohibit cloning
-	private function __clone() {
-	}
-
-	//! Prohibit instantiation
-	private function __construct() {
-	}
-
-}
-
-return Base::instance();

+ 0 - 384
frameworks/PHP/fat-free/src/db/cursor.php

@@ -1,384 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB;
-
-//! Simple cursor implementation
-abstract class Cursor extends \Magic implements \IteratorAggregate {
-
-	//@{ Error messages
-	const
-		E_Field='Undefined field %s';
-	//@}
-
-	protected
-		//! Query results
-		$query=array(),
-		//! Current position
-		$ptr=0,
-		//! Event listeners
-		$trigger=array();
-
-	/**
-	*	Return database type
-	*	@return string
-	**/
-	abstract function dbtype();
-
-	/**
-	*	Return field names
-	*	@return array
-	**/
-	abstract function fields();
-
-	/**
-	*	Return fields of mapper object as an associative array
-	*	@return array
-	*	@param $obj object
-	**/
-	abstract function cast($obj=NULL);
-
-	/**
-	*	Return records (array of mapper objects) that match criteria
-	*	@return array
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	abstract function find($filter=NULL,array $options=NULL,$ttl=0);
-
-	/**
-	*	Count records that match criteria
-	*	@return int
-	*	@param $filter array
-	*	@param $ttl int
-	**/
-	abstract function count($filter=NULL,$ttl=0);
-
-	/**
-	*	Insert new record
-	*	@return array
-	**/
-	abstract function insert();
-
-	/**
-	*	Update current record
-	*	@return array
-	**/
-	abstract function update();
-
-	/**
-	*	Hydrate mapper object using hive array variable
-	*	@return NULL
-	*	@param $var array|string
-	*	@param $func callback
-	**/
-	abstract function copyfrom($var,$func=NULL);
-
-	/**
-	*	Populate hive array variable with mapper fields
-	*	@return NULL
-	*	@param $key string
-	**/
-	abstract function copyto($key);
-
-	/**
-	*	Get cursor's equivalent external iterator
-	*	Causes a fatal error in PHP 5.3.5if uncommented
-	*	return ArrayIterator
-	**/
-	abstract function getiterator();
-
-
-	/**
-	*	Return TRUE if current cursor position is not mapped to any record
-	*	@return bool
-	**/
-	function dry() {
-		return empty($this->query[$this->ptr]);
-	}
-
-	/**
-	*	Return first record (mapper object) that matches criteria
-	*	@return \DB\Cursor|FALSE
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function findone($filter=NULL,array $options=NULL,$ttl=0) {
-		if (!$options)
-			$options=array();
-		// Override limit
-		$options['limit']=1;
-		return ($data=$this->find($filter,$options,$ttl))?$data[0]:FALSE;
-	}
-
-	/**
-	*	Return array containing subset of records matching criteria,
-	*	total number of records in superset, specified limit, number of
-	*	subsets available, and actual subset position
-	*	@return array
-	*	@param $pos int
-	*	@param $size int
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function paginate(
-		$pos=0,$size=10,$filter=NULL,array $options=NULL,$ttl=0) {
-		$total=$this->count($filter,$ttl);
-		$count=ceil($total/$size);
-		$pos=max(0,min($pos,$count-1));
-		return array(
-			'subset'=>$this->find($filter,
-				array_merge(
-					$options?:array(),
-					array('limit'=>$size,'offset'=>$pos*$size)
-				),
-				$ttl
-			),
-			'total'=>$total,
-			'limit'=>$size,
-			'count'=>$count,
-			'pos'=>$pos<$count?$pos:0
-		);
-	}
-
-	/**
-	*	Map to first record that matches criteria
-	*	@return array|FALSE
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function load($filter=NULL,array $options=NULL,$ttl=0) {
-		return ($this->query=$this->find($filter,$options,$ttl)) &&
-			$this->skip(0)?$this->query[$this->ptr=0]:FALSE;
-	}
-
-	/**
-	*	Return the count of records loaded
-	*	@return int
-	**/
-	function loaded() {
-		return count($this->query);
-	}
-
-	/**
-	*	Map to first record in cursor
-	*	@return mixed
-	**/
-	function first() {
-		return $this->skip(-$this->ptr);
-	}
-
-	/**
-	*	Map to last record in cursor
-	*	@return mixed
-	**/
-	function last() {
-		return $this->skip(($ofs=count($this->query)-$this->ptr)?$ofs-1:0);
-	}
-
-	/**
-	*	Map to nth record relative to current cursor position
-	*	@return mixed
-	*	@param $ofs int
-	**/
-	function skip($ofs=1) {
-		$this->ptr+=$ofs;
-		return $this->ptr>-1 && $this->ptr<count($this->query)?
-			$this->query[$this->ptr]:FALSE;
-	}
-
-	/**
-	*	Map next record
-	*	@return mixed
-	**/
-	function next() {
-		return $this->skip();
-	}
-
-	/**
-	*	Map previous record
-	*	@return mixed
-	**/
-	function prev() {
-		return $this->skip(-1);
-	}
-
-	/**
-	 * Return whether current iterator position is valid.
-	 */
-	function valid() {
-		return !$this->dry();
-	}
-
-	/**
-	*	Save mapped record
-	*	@return mixed
-	**/
-	function save() {
-		return $this->query?$this->update():$this->insert();
-	}
-
-	/**
-	*	Delete current record
-	*	@return int|bool
-	**/
-	function erase() {
-		$this->query=array_slice($this->query,0,$this->ptr,TRUE)+
-			array_slice($this->query,$this->ptr,NULL,TRUE);
-		$this->skip(0);
-	}
-
-	/**
-	*	Define onload trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function onload($func) {
-		return $this->trigger['load']=$func;
-	}
-
-	/**
-	*	Define beforeinsert trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function beforeinsert($func) {
-		return $this->trigger['beforeinsert']=$func;
-	}
-
-	/**
-	*	Define afterinsert trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function afterinsert($func) {
-		return $this->trigger['afterinsert']=$func;
-	}
-
-	/**
-	*	Define oninsert trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function oninsert($func) {
-		return $this->afterinsert($func);
-	}
-
-	/**
-	*	Define beforeupdate trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function beforeupdate($func) {
-		return $this->trigger['beforeupdate']=$func;
-	}
-
-	/**
-	*	Define afterupdate trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function afterupdate($func) {
-		return $this->trigger['afterupdate']=$func;
-	}
-
-	/**
-	*	Define onupdate trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function onupdate($func) {
-		return $this->afterupdate($func);
-	}
-
-	/**
-	*	Define beforesave trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function beforesave($func) {
-		$this->trigger['beforeinsert']=$func;
-		$this->trigger['beforeupdate']=$func;
-		return $func;
-	}
-
-	/**
-	*	Define aftersave trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function aftersave($func) {
-		$this->trigger['afterinsert']=$func;
-		$this->trigger['afterupdate']=$func;
-		return $func;
-	}
-
-	/**
-	*	Define onsave trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function onsave($func) {
-		return $this->aftersave($func);
-	}
-
-	/**
-	*	Define beforeerase trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function beforeerase($func) {
-		return $this->trigger['beforeerase']=$func;
-	}
-
-	/**
-	*	Define aftererase trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function aftererase($func) {
-		return $this->trigger['aftererase']=$func;
-	}
-
-	/**
-	*	Define onerase trigger
-	*	@return callback
-	*	@param $func callback
-	**/
-	function onerase($func) {
-		return $this->aftererase($func);
-	}
-
-	/**
-	*	Reset cursor
-	*	@return NULL
-	**/
-	function reset() {
-		$this->query=array();
-		$this->ptr=0;
-	}
-
-}

+ 0 - 111
frameworks/PHP/fat-free/src/db/mongo.php

@@ -1,111 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB;
-
-//! MongoDB wrapper
-class Mongo {
-
-	//@{
-	const
-		E_Profiler='MongoDB profiler is disabled';
-	//@}
-
-	protected
-		//! UUID
-		$uuid,
-		//! Data source name
-		$dsn,
-		//! MongoDB object
-		$db,
-		//! MongoDB log
-		$log;
-
-	/**
-	*	Return data source name
-	*	@return string
-	**/
-	function dsn() {
-		return $this->dsn;
-	}
-
-	/**
-	*	Return UUID
-	*	@return string
-	**/
-	function uuid() {
-		return $this->uuid;
-	}
-
-	/**
-	*	Return MongoDB profiler results
-	*	@return string
-	**/
-	function log() {
-		$cursor=$this->selectcollection('system.profile')->find();
-		foreach (iterator_to_array($cursor) as $frame)
-			if (!preg_match('/\.system\..+$/',$frame['ns']))
-				$this->log.=date('r',$frame['ts']->sec).' ('.
-					sprintf('%.1f',$frame['millis']).'ms) '.
-					$frame['ns'].' ['.$frame['op'].'] '.
-					(empty($frame['query'])?
-						'':json_encode($frame['query'])).
-					(empty($frame['command'])?
-						'':json_encode($frame['command'])).
-					PHP_EOL;
-		return $this->log;
-	}
-
-	/**
-	*	Intercept native call to re-enable profiler
-	*	@return int
-	**/
-	function drop() {
-		$out=$this->db->drop();
-		$this->setprofilinglevel(2);
-		return $out;
-	}
-
-	/**
-	*	Redirect call to MongoDB object
-	*	@return mixed
-	*	@param $func string
-	*	@param $args array
-	**/
-	function __call($func,array $args) {
-		return call_user_func_array(array($this->db,$func),$args);
-	}
-
-	/**
-	*	Instantiate class
-	*	@param $dsn string
-	*	@param $dbname string
-	*	@param $options array
-	**/
-	function __construct($dsn,$dbname,array $options=NULL) {
-		$this->uuid=\Base::instance()->hash($this->dsn=$dsn);
-		$class=class_exists('\MongoClient')?'\MongoClient':'\Mongo';
-		$this->db=new \MongoDB(new $class($dsn,$options?:array()),$dbname);
-		$this->setprofilinglevel(2);
-	}
-
-}

+ 0 - 361
frameworks/PHP/fat-free/src/db/mongo/mapper.php

@@ -1,361 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB\Mongo;
-
-//! MongoDB mapper
-class Mapper extends \DB\Cursor {
-
-	protected
-		//! MongoDB wrapper
-		$db,
-		//! Mongo collection
-		$collection,
-		//! Mongo document
-		$document=array(),
-		//! Mongo cursor
-		$cursor;
-
-	/**
-	*	Return database type
-	*	@return string
-	**/
-	function dbtype() {
-		return 'Mongo';
-	}
-
-	/**
-	*	Return TRUE if field is defined
-	*	@return bool
-	*	@param $key string
-	**/
-	function exists($key) {
-		return array_key_exists($key,$this->document);
-	}
-
-	/**
-	*	Assign value to field
-	*	@return scalar|FALSE
-	*	@param $key string
-	*	@param $val scalar
-	**/
-	function set($key,$val) {
-		return $this->document[$key]=$val;
-	}
-
-	/**
-	*	Retrieve value of field
-	*	@return scalar|FALSE
-	*	@param $key string
-	**/
-	function &get($key) {
-		if ($this->exists($key))
-			return $this->document[$key];
-		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
-	}
-
-	/**
-	*	Delete field
-	*	@return NULL
-	*	@param $key string
-	**/
-	function clear($key) {
-		unset($this->document[$key]);
-	}
-
-	/**
-	*	Convert array to mapper object
-	*	@return \DB\Mongo\Mapper
-	*	@param $row array
-	**/
-	protected function factory($row) {
-		$mapper=clone($this);
-		$mapper->reset();
-		foreach ($row as $key=>$val)
-			$mapper->document[$key]=$val;
-		$mapper->query=array(clone($mapper));
-		if (isset($mapper->trigger['load']))
-			\Base::instance()->call($mapper->trigger['load'],$mapper);
-		return $mapper;
-	}
-
-	/**
-	*	Return fields of mapper object as an associative array
-	*	@return array
-	*	@param $obj object
-	**/
-	function cast($obj=NULL) {
-		if (!$obj)
-			$obj=$this;
-		return $obj->document;
-	}
-
-	/**
-	*	Build query and execute
-	*	@return \DB\Mongo\Mapper[]
-	*	@param $fields string
-	*	@param $filter array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function select($fields=NULL,$filter=NULL,array $options=NULL,$ttl=0) {
-		if (!$options)
-			$options=array();
-		$options+=array(
-			'group'=>NULL,
-			'order'=>NULL,
-			'limit'=>0,
-			'offset'=>0
-		);
-		$fw=\Base::instance();
-		$cache=\Cache::instance();
-		if (!($cached=$cache->exists($hash=$fw->hash($this->db->dsn().
-			$fw->stringify(array($fields,$filter,$options))).'.mongo',
-			$result)) || !$ttl || $cached[0]+$ttl<microtime(TRUE)) {
-			if ($options['group']) {
-				$grp=$this->collection->group(
-					$options['group']['keys'],
-					$options['group']['initial'],
-					$options['group']['reduce'],
-					array(
-						'condition'=>$filter,
-						'finalize'=>$options['group']['finalize']
-					)
-				);
-				$tmp=$this->db->selectcollection(
-					$fw->get('HOST').'.'.$fw->get('BASE').'.'.
-					uniqid(NULL,TRUE).'.tmp'
-				);
-				$tmp->batchinsert($grp['retval'],array('w'=>1));
-				$filter=array();
-				$collection=$tmp;
-			}
-			else {
-				$filter=$filter?:array();
-				$collection=$this->collection;
-			}
-			$this->cursor=$collection->find($filter,$fields?:array());
-			if ($options['order'])
-				$this->cursor=$this->cursor->sort($options['order']);
-			if ($options['limit'])
-				$this->cursor=$this->cursor->limit($options['limit']);
-			if ($options['offset'])
-				$this->cursor=$this->cursor->skip($options['offset']);
-			$result=array();
-			while ($this->cursor->hasnext())
-				$result[]=$this->cursor->getnext();
-			if ($options['group'])
-				$tmp->drop();
-			if ($fw->get('CACHE') && $ttl)
-				// Save to cache backend
-				$cache->set($hash,$result,$ttl);
-		}
-		$out=array();
-		foreach ($result as $doc)
-			$out[]=$this->factory($doc);
-		return $out;
-	}
-
-	/**
-	*	Return records that match criteria
-	*	@return \DB\Mongo\Mapper[]
-	*	@param $filter array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function find($filter=NULL,array $options=NULL,$ttl=0) {
-		if (!$options)
-			$options=array();
-		$options+=array(
-			'group'=>NULL,
-			'order'=>NULL,
-			'limit'=>0,
-			'offset'=>0
-		);
-		return $this->select(NULL,$filter,$options,$ttl);
-	}
-
-	/**
-	*	Count records that match criteria
-	*	@return int
-	*	@param $filter array
-	*	@param $ttl int
-	**/
-	function count($filter=NULL,$ttl=0) {
-		$fw=\Base::instance();
-		$cache=\Cache::instance();
-		if (!($cached=$cache->exists($hash=$fw->hash($fw->stringify(
-			array($filter))).'.mongo',$result)) || !$ttl ||
-			$cached[0]+$ttl<microtime(TRUE)) {
-			$result=$this->collection->count($filter?:array());
-			if ($fw->get('CACHE') && $ttl)
-				// Save to cache backend
-				$cache->set($hash,$result,$ttl);
-		}
-		return $result;
-	}
-
-	/**
-	*	Return record at specified offset using criteria of previous
-	*	load() call and make it active
-	*	@return array
-	*	@param $ofs int
-	**/
-	function skip($ofs=1) {
-		$this->document=($out=parent::skip($ofs))?$out->document:array();
-		if ($this->document && isset($this->trigger['load']))
-			\Base::instance()->call($this->trigger['load'],$this);
-		return $out;
-	}
-
-	/**
-	*	Insert new record
-	*	@return array
-	**/
-	function insert() {
-		if (isset($this->document['_id']))
-			return $this->update();
-		if (isset($this->trigger['beforeinsert']) &&
-			\Base::instance()->call($this->trigger['beforeinsert'],
-				array($this,array('_id'=>$this->document['_id'])))===FALSE)
-			return $this->document;
-		$this->collection->insert($this->document);
-		$pkey=array('_id'=>$this->document['_id']);
-		if (isset($this->trigger['afterinsert']))
-			\Base::instance()->call($this->trigger['afterinsert'],
-				array($this,$pkey));
-		$this->load($pkey);
-		return $this->document;
-	}
-
-	/**
-	*	Update current record
-	*	@return array
-	**/
-	function update() {
-		$pkey=array('_id'=>$this->document['_id']);
-		if (isset($this->trigger['beforeupdate']) &&
-			\Base::instance()->call($this->trigger['beforeupdate'],
-				array($this,$pkey))===FALSE)
-			return $this->document;
-		$this->collection->update(
-			$pkey,$this->document,array('upsert'=>TRUE));
-		if (isset($this->trigger['afterupdate']))
-			\Base::instance()->call($this->trigger['afterupdate'],
-				array($this,$pkey));
-		return $this->document;
-	}
-
-	/**
-	*	Delete current record
-	*	@return bool
-	*	@param $filter array
-	**/
-	function erase($filter=NULL) {
-		if ($filter)
-			return $this->collection->remove($filter);
-		$pkey=array('_id'=>$this->document['_id']);
-		if (isset($this->trigger['beforeerase']) &&
-			\Base::instance()->call($this->trigger['beforeerase'],
-				array($this,$pkey))===FALSE)
-			return FALSE;
-		$result=$this->collection->
-			remove(array('_id'=>$this->document['_id']));
-		parent::erase();
-		if (isset($this->trigger['aftererase']))
-			\Base::instance()->call($this->trigger['aftererase'],
-				array($this,$pkey));
-		return $result;
-	}
-
-	/**
-	*	Reset cursor
-	*	@return NULL
-	**/
-	function reset() {
-		$this->document=array();
-		parent::reset();
-	}
-
-	/**
-	*	Hydrate mapper object using hive array variable
-	*	@return NULL
-	*	@param $var array|string
-	*	@param $func callback
-	**/
-	function copyfrom($var,$func=NULL) {
-		if (is_string($var))
-			$var=\Base::instance()->get($var);
-		if ($func)
-			$var=call_user_func($func,$var);
-		foreach ($var as $key=>$val)
-			$this->document[$key]=$val;
-	}
-
-	/**
-	*	Populate hive array variable with mapper fields
-	*	@return NULL
-	*	@param $key string
-	**/
-	function copyto($key) {
-		$var=&\Base::instance()->ref($key);
-		foreach ($this->document as $key=>$field)
-			$var[$key]=$field;
-	}
-
-	/**
-	*	Return field names
-	*	@return array
-	**/
-	function fields() {
-		return array_keys($this->document);
-	}
-
-	/**
-	*	Return the cursor from last query
-	*	@return object|NULL
-	**/
-	function cursor() {
-		return $this->cursor;
-	}
-
-	/**
-	*	Retrieve external iterator for fields
-	*	@return object
-	**/
-	function getiterator() {
-		return new \ArrayIterator($this->cast());
-	}
-
-	/**
-	*	Instantiate class
-	*	@return void
-	*	@param $db object
-	*	@param $collection string
-	**/
-	function __construct(\DB\Mongo $db,$collection) {
-		$this->db=$db;
-		$this->collection=$db->selectcollection($collection);
-		$this->reset();
-	}
-
-}

+ 0 - 180
frameworks/PHP/fat-free/src/db/mongo/session.php

@@ -1,180 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB\Mongo;
-
-//! MongoDB-managed session handler
-class Session extends Mapper {
-
-	protected
-		//! Session ID
-		$sid;
-
-	/**
-	*	Open session
-	*	@return TRUE
-	*	@param $path string
-	*	@param $name string
-	**/
-	function open($path,$name) {
-		return TRUE;
-	}
-
-	/**
-	*	Close session
-	*	@return TRUE
-	**/
-	function close() {
-		return TRUE;
-	}
-
-	/**
-	*	Return session data in serialized format
-	*	@return string|FALSE
-	*	@param $id string
-	**/
-	function read($id) {
-		if ($id!=$this->sid)
-			$this->load(array('session_id'=>$this->sid=$id));
-		return $this->dry()?FALSE:$this->get('data');
-	}
-
-	/**
-	*	Write session data
-	*	@return TRUE
-	*	@param $id string
-	*	@param $data string
-	**/
-	function write($id,$data) {
-		$fw=\Base::instance();
-		$sent=headers_sent();
-		$headers=$fw->get('HEADERS');
-		if ($id!=$this->sid)
-			$this->load(array('session_id'=>$this->sid=$id));
-		$csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
-			$fw->hash(mt_rand());
-		$this->set('session_id',$id);
-		$this->set('data',$data);
-		$this->set('csrf',$sent?$this->csrf():$csrf);
-		$this->set('ip',$fw->get('IP'));
-		$this->set('agent',
-			isset($headers['User-Agent'])?$headers['User-Agent']:'');
-		$this->set('stamp',time());
-		$this->save();
-		return TRUE;
-	}
-
-	/**
-	*	Destroy session
-	*	@return TRUE
-	*	@param $id string
-	**/
-	function destroy($id) {
-		$this->erase(array('session_id'=>$id));
-		setcookie(session_name(),'',strtotime('-1 year'));
-		unset($_COOKIE[session_name()]);
-		header_remove('Set-Cookie');
-		return TRUE;
-	}
-
-	/**
-	*	Garbage collector
-	*	@return TRUE
-	*	@param $max int
-	**/
-	function cleanup($max) {
-		$this->erase(array('$where'=>'this.stamp+'.$max.'<'.time()));
-		return TRUE;
-	}
-
-	/**
-	*	Return anti-CSRF token
-	*	@return string|FALSE
-	**/
-	function csrf() {
-		return $this->dry()?FALSE:$this->get('csrf');
-	}
-
-	/**
-	*	Return IP address
-	*	@return string|FALSE
-	**/
-	function ip() {
-		return $this->dry()?FALSE:$this->get('ip');
-	}
-
-	/**
-	*	Return Unix timestamp
-	*	@return string|FALSE
-	**/
-	function stamp() {
-		return $this->dry()?FALSE:$this->get('stamp');
-	}
-
-	/**
-	*	Return HTTP user agent
-	*	@return string|FALSE
-	**/
-	function agent() {
-		return $this->dry()?FALSE:$this->get('agent');
-	}
-
-	/**
-	*	Instantiate class
-	*	@param $db object
-	*	@param $table string
-	*	@param $onsuspect callback
-	**/
-	function __construct(\DB\Mongo $db,$table='sessions',$onsuspect=NULL) {
-		parent::__construct($db,$table);
-		session_set_save_handler(
-			array($this,'open'),
-			array($this,'close'),
-			array($this,'read'),
-			array($this,'write'),
-			array($this,'destroy'),
-			array($this,'cleanup')
-		);
-		register_shutdown_function('session_commit');
-		@session_start();
-		$fw=\Base::instance();
-		$headers=$fw->get('HEADERS');
-		if (($ip=$this->ip()) && $ip!=$fw->get('IP') ||
-			($agent=$this->agent()) &&
-			(!isset($headers['User-Agent']) ||
-				$agent!=$headers['User-Agent'])) {
-			if (isset($onsuspect))
-				$fw->call($onsuspect,array($this));
-			else {
-				session_destroy();
-				$fw->error(403);
-			}
-		}
-		$csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
-			$fw->hash(mt_rand());
-		if ($this->load(array('session_id'=>$this->sid=session_id()))) {
-			$this->set('csrf',$csrf);
-			$this->save();
-		}
-	}
-
-}

+ 0 - 455
frameworks/PHP/fat-free/src/db/sql.php

@@ -1,455 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB;
-
-//! PDO wrapper
-class SQL {
-
-	//@{ Error messages
-	const
-		E_PKey='Table %s does not have a primary key';
-	//@}
-
-	protected
-		//! UUID
-		$uuid,
-		//! Raw PDO
-		$pdo,
-		//! Data source name
-		$dsn,
-		//! Database engine
-		$engine,
-		//! Database name
-		$dbname,
-		//! Transaction flag
-		$trans=FALSE,
-		//! Number of rows affected by query
-		$rows=0,
-		//! SQL log
-		$log;
-
-	/**
-	*	Begin SQL transaction
-	*	@return bool
-	**/
-	function begin() {
-		$out=$this->pdo->begintransaction();
-		$this->trans=TRUE;
-		return $out;
-	}
-
-	/**
-	*	Rollback SQL transaction
-	*	@return bool
-	**/
-	function rollback() {
-		$out=$this->pdo->rollback();
-		$this->trans=FALSE;
-		return $out;
-	}
-
-	/**
-	*	Commit SQL transaction
-	*	@return bool
-	**/
-	function commit() {
-		$out=$this->pdo->commit();
-		$this->trans=FALSE;
-		return $out;
-	}
-
-	/**
-	*	Map data type of argument to a PDO constant
-	*	@return int
-	*	@param $val scalar
-	**/
-	function type($val) {
-		switch (gettype($val)) {
-			case 'NULL':
-				return \PDO::PARAM_NULL;
-			case 'boolean':
-				return \PDO::PARAM_BOOL;
-			case 'integer':
-				return \PDO::PARAM_INT;
-			default:
-				return \PDO::PARAM_STR;
-		}
-	}
-
-	/**
-	*	Cast value to PHP type
-	*	@return scalar
-	*	@param $type string
-	*	@param $val scalar
-	**/
-	function value($type,$val) {
-		switch ($type) {
-			case \PDO::PARAM_NULL:
-				return (unset)$val;
-			case \PDO::PARAM_INT:
-				return (int)$val;
-			case \PDO::PARAM_BOOL:
-				return (bool)$val;
-			case \PDO::PARAM_STR:
-				return (string)$val;
-		}
-	}
-
-	/**
-	*	Execute SQL statement(s)
-	*	@return array|int|FALSE
-	*	@param $cmds string|array
-	*	@param $args string|array
-	*	@param $ttl int
-	*	@param $log bool
-	**/
-	function exec($cmds,$args=NULL,$ttl=0,$log=TRUE) {
-		$auto=FALSE;
-		if (is_null($args))
-			$args=array();
-		elseif (is_scalar($args))
-			$args=array(1=>$args);
-		if (is_array($cmds)) {
-			if (count($args)<($count=count($cmds)))
-				// Apply arguments to SQL commands
-				$args=array_fill(0,$count,$args);
-			if (!$this->trans) {
-				$this->begin();
-				$auto=TRUE;
-			}
-		}
-		else {
-			$count=1;
-			$cmds=array($cmds);
-			$args=array($args);
-		}
-		$fw=\Base::instance();
-		$cache=\Cache::instance();
-		$result=FALSE;
-		for ($i=0;$i<$count;$i++) {
-			$cmd=$cmds[$i];
-			$arg=$args[$i];
-			if (!preg_replace('/(^\s+|[\s;]+$)/','',$cmd))
-				continue;
-			$now=microtime(TRUE);
-			$keys=$vals=array();
-			if ($fw->get('CACHE') && $ttl && ($cached=$cache->exists(
-				$hash=$fw->hash($this->dsn.$cmd.
-				$fw->stringify($arg)).'.sql',$result)) &&
-				$cached[0]+$ttl>microtime(TRUE)) {
-				foreach ($arg as $key=>$val) {
-					$vals[]=$fw->stringify(is_array($val)?$val[0]:$val);
-					$keys[]='/'.preg_quote(is_numeric($key)?chr(0).'?':$key).
-						'/';
-				}
-				if ($log)
-					$this->log.=date('r').' ('.
-						sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
-						'[CACHED] '.
-						preg_replace($keys,$vals,
-							str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
-			}
-			elseif (is_object($query=$this->pdo->prepare($cmd))) {
-				foreach ($arg as $key=>$val) {
-					if (is_array($val)) {
-						// User-specified data type
-						$query->bindvalue($key,$val[0],$val[1]);
-						$vals[]=$fw->stringify($this->value($val[1],$val[0]));
-					}
-					else {
-						// Convert to PDO data type
-						$query->bindvalue($key,$val,
-							$type=$this->type($val));
-						$vals[]=$fw->stringify($this->value($type,$val));
-					}
-					$keys[]='/'.preg_quote(is_numeric($key)?chr(0).'?':$key).
-						'/';
-				}
-				if ($log)
-					$this->log.=date('r').' ('.
-						sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
-						preg_replace($keys,$vals,
-							str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
-				$query->execute();
-				$error=$query->errorinfo();
-				if ($error[0]!=\PDO::ERR_NONE) {
-					// Statement-level error occurred
-					if ($this->trans)
-						$this->rollback();
-					user_error('PDOStatement: '.$error[2],E_USER_ERROR);
-				}
-				if (preg_match('/^\s*'.
-					'(?:EXPLAIN|SELECT|PRAGMA|SHOW|RETURNING)\b/is',$cmd) ||
-					(preg_match('/^\s*(?:CALL|EXEC)\b/is',$cmd) &&
-						$query->columnCount())) {
-					$result=$query->fetchall(\PDO::FETCH_ASSOC);
-					// Work around SQLite quote bug
-					if (preg_match('/sqlite2?/',$this->engine))
-						foreach ($result as $pos=>$rec) {
-							unset($result[$pos]);
-							$result[$pos]=array();
-							foreach ($rec as $key=>$val)
-								$result[$pos][trim($key,'\'"[]`')]=$val;
-						}
-					$this->rows=count($result);
-					if ($fw->get('CACHE') && $ttl)
-						// Save to cache backend
-						$cache->set($hash,$result,$ttl);
-				}
-				else
-					$this->rows=$result=$query->rowcount();
-				$query->closecursor();
-				unset($query);
-			}
-			else {
-				$error=$this->errorinfo();
-				if ($error[0]!=\PDO::ERR_NONE) {
-					// PDO-level error occurred
-					if ($this->trans)
-						$this->rollback();
-					user_error('PDO: '.$error[2],E_USER_ERROR);
-				}
-			}
-		}
-		if ($this->trans && $auto)
-			$this->commit();
-		return $result;
-	}
-
-	/**
-	*	Return number of rows affected by last query
-	*	@return int
-	**/
-	function count() {
-		return $this->rows;
-	}
-
-	/**
-	*	Return SQL profiler results
-	*	@return string
-	**/
-	function log() {
-		return $this->log;
-	}
-
-	/**
-	*	Retrieve schema of SQL table
-	*	@return array|FALSE
-	*	@param $table string
-	*	@param $fields array|string
-	*	@param $ttl int
-	**/
-	function schema($table,$fields=NULL,$ttl=0) {
-		if (strpos($table,'.'))
-			list($schema,$table)=explode('.',$table);
-		// Supported engines
-		$cmd=array(
-			'sqlite2?'=>array(
-				'PRAGMA table_info("'.$table.'");',
-				'name','type','dflt_value','notnull',0,'pk',TRUE),
-			'mysql'=>array(
-				'SHOW columns FROM `'.$this->dbname.'`.`'.$table.'`;',
-				'Field','Type','Default','Null','YES','Key','PRI'),
-			'mssql|sqlsrv|sybase|dblib|pgsql|odbc'=>array(
-				'SELECT '.
-					'c.column_name AS field,'.
-					'c.data_type AS type,'.
-					'c.column_default AS defval,'.
-					'c.is_nullable AS nullable,'.
-					't.constraint_type AS pkey '.
-				'FROM information_schema.columns AS c '.
-				'LEFT OUTER JOIN '.
-					'information_schema.key_column_usage AS k '.
-					'ON '.
-						'c.table_name=k.table_name AND '.
-						'c.column_name=k.column_name AND '.
-						'c.table_schema=k.table_schema '.
-						($this->dbname?
-							('AND c.table_catalog=k.table_catalog '):'').
-				'LEFT OUTER JOIN '.
-					'information_schema.table_constraints AS t ON '.
-						'k.table_name=t.table_name AND '.
-						'k.constraint_name=t.constraint_name AND '.
-						'k.table_schema=t.table_schema '.
-						($this->dbname?
-							('AND k.table_catalog=t.table_catalog '):'').
-				'WHERE '.
-					'c.table_name='.$this->quote($table).
-					($this->dbname?
-						(' AND c.table_catalog='.
-							$this->quote($this->dbname)):'').
-				';',
-				'field','type','defval','nullable','YES','pkey','PRIMARY KEY'),
-			'oci'=>array(
-				'SELECT c.column_name AS field, '.
-					'c.data_type AS type, '.
-					'c.data_default AS defval, '.
-					'c.nullable AS nullable, '.
-					'(SELECT t.constraint_type '.
-						'FROM all_cons_columns acc '.
-						'LEFT OUTER JOIN all_constraints t '.
-						'ON acc.constraint_name=t.constraint_name '.
-						'WHERE acc.table_name='.$this->quote($table).' '.
-						'AND acc.column_name=c.column_name '.
-						'AND constraint_type='.$this->quote('P').') AS pkey '.
-				'FROM all_tab_cols c '.
-				'WHERE c.table_name='.$this->quote($table),
-				'FIELD','TYPE','DEFVAL','NULLABLE','Y','PKEY','P')
-		);
-		if (is_string($fields))
-			$fields=\Base::instance()->split($fields);
-		foreach ($cmd as $key=>$val)
-			if (preg_match('/'.$key.'/',$this->engine)) {
-				// Improve InnoDB performance on MySQL with
-				// SET GLOBAL innodb_stats_on_metadata=0;
-				// This requires SUPER privilege!
-				$rows=array();
-				foreach ($this->exec($val[0],NULL,$ttl) as $row) {
-					if (!$fields || in_array($row[$val[1]],$fields))
-						$rows[$row[$val[1]]]=array(
-							'type'=>$row[$val[2]],
-							'pdo_type'=>
-								preg_match('/int\b|integer/i',$row[$val[2]])?
-									\PDO::PARAM_INT:
-									(preg_match('/bool/i',$row[$val[2]])?
-										\PDO::PARAM_BOOL:
-										\PDO::PARAM_STR),
-							'default'=>is_string($row[$val[3]])?
-								preg_replace('/^\s*([\'"])(.*)\1\s*/','\2',
-								$row[$val[3]]):$row[$val[3]],
-							'nullable'=>$row[$val[4]]==$val[5],
-							'pkey'=>$row[$val[6]]==$val[7]
-						);
-				}
-				return $rows;
-			}
-		user_error(sprintf(self::E_PKey,$table),E_USER_ERROR);
-		return FALSE;
-	}
-
-	/**
-	*	Quote string
-	*	@return string
-	*	@param $val mixed
-	*	@param $type int
-	**/
-	function quote($val,$type=\PDO::PARAM_STR) {
-		return $this->engine=='odbc'?
-			(is_string($val)?
-				\Base::instance()->stringify(str_replace('\'','\'\'',$val)):
-				$val):
-			$this->pdo->quote($val,$type);
-	}
-
-	/**
-	*	Return UUID
-	*	@return string
-	**/
-	function uuid() {
-		return $this->uuid;
-	}
-
-	/**
-	*	Return parent object
-	*	@return \PDO
-	**/
-	function pdo() {
-		return $this->pdo;
-	}
-
-	/**
-	*	Return database engine
-	*	@return string
-	**/
-	function driver() {
-		return $this->engine;
-	}
-
-	/**
-	*	Return server version
-	*	@return string
-	**/
-	function version() {
-		return $this->pdo->getattribute(\PDO::ATTR_SERVER_VERSION);
-	}
-
-	/**
-	*	Return database name
-	*	@return string
-	**/
-	function name() {
-		return $this->dbname;
-	}
-
-	/**
-	*	Return quoted identifier name
-	*	@return string
-	*	@param $key
-	**/
-	function quotekey($key) {
-		$delims=array(
-			'mysql'=>'``',
-			'sqlite2?|pgsql|oci'=>'""',
-			'mssql|sqlsrv|odbc|sybase|dblib'=>'[]'
-		);
-		$use='';
-		foreach ($delims as $engine=>$delim)
-			if (preg_match('/'.$engine.'/',$this->engine)) {
-				$use=$delim;
-				break;
-			}
-		return $use[0].implode($use[1].'.'.$use[0],explode('.',$key)).$use[1];
-	}
-
-	/**
-	*	Redirect call to MongoDB object
-	*	@return mixed
-	*	@param $func string
-	*	@param $args array
-	**/
-	function __call($func,array $args) {
-		return call_user_func_array(array($this->pdo,$func),$args);
-	}
-
-	/**
-	*	Instantiate class
-	*	@param $dsn string
-	*	@param $user string
-	*	@param $pw string
-	*	@param $options array
-	**/
-	function __construct($dsn,$user=NULL,$pw=NULL,array $options=NULL) {
-		$fw=\Base::instance();
-		$this->uuid=$fw->hash($this->dsn=$dsn);
-		if (preg_match('/^.+?(?:dbname|database)=(.+?)(?=;|$)/is',$dsn,$parts))
-			$this->dbname=$parts[1];
-		if (!$options)
-			$options=array();
-		if (isset($parts[0]) && strstr($parts[0],':',TRUE)=='mysql')
-			$options+=array(\PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES '.
-				strtolower(str_replace('-','',$fw->get('ENCODING'))).';');
-		$this->pdo=new \PDO($dsn,$user,$pw,$options);
-		$this->engine=$this->pdo->getattribute(\PDO::ATTR_DRIVER_NAME);
-	}
-
-}

+ 0 - 639
frameworks/PHP/fat-free/src/db/sql/mapper.php

@@ -1,639 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB\SQL;
-
-//! SQL data mapper
-class Mapper extends \DB\Cursor {
-
-	protected
-		//! PDO wrapper
-		$db,
-		//! Database engine
-		$engine,
-		//! SQL table
-		$source,
-		//! SQL table (quoted)
-		$table,
-		//! Last insert ID
-		$_id,
-		//! Defined fields
-		$fields,
-		//! Adhoc fields
-		$adhoc=array();
-
-	/**
-	*	Return database type
-	*	@return string
-	**/
-	function dbtype() {
-		return 'SQL';
-	}
-
-	/**
-	*	Return mapped table
-	*	@return string
-	**/
-	function table() {
-		return $this->source;
-	}
-
-	/**
-	*	Return TRUE if field is defined
-	*	@return bool
-	*	@param $key string
-	**/
-	function exists($key) {
-		return array_key_exists($key,$this->fields+$this->adhoc);
-	}
-
-	/**
-	*	Return TRUE if any/specified field value has changed
-	*	@return bool
-	*	@param $key string
-	**/
-	function changed($key=NULL) {
-		if (isset($key))
-			return $this->fields[$key]['changed'];
-		foreach($this->fields as $key=>$field)
-			if ($field['changed'])
-				return TRUE;
-		return FALSE;
-	}
-
-	/**
-	*	Assign value to field
-	*	@return scalar
-	*	@param $key string
-	*	@param $val scalar
-	**/
-	function set($key,$val) {
-		if (array_key_exists($key,$this->fields)) {
-			$val=is_null($val) && $this->fields[$key]['nullable']?
-				NULL:$this->db->value($this->fields[$key]['pdo_type'],$val);
-			if ($this->fields[$key]['value']!==$val ||
-				$this->fields[$key]['default']!==$val && is_null($val))
-				$this->fields[$key]['changed']=TRUE;
-			return $this->fields[$key]['value']=$val;
-		}
-		// adjust result on existing expressions
-		if (isset($this->adhoc[$key]))
-			$this->adhoc[$key]['value']=$val;
-		else
-			// Parenthesize expression in case it's a subquery
-			$this->adhoc[$key]=array('expr'=>'('.$val.')','value'=>NULL);
-		return $val;
-	}
-
-	/**
-	*	Retrieve value of field
-	*	@return scalar
-	*	@param $key string
-	**/
-	function &get($key) {
-		if ($key=='_id')
-			return $this->_id;
-		elseif (array_key_exists($key,$this->fields))
-			return $this->fields[$key]['value'];
-		elseif (array_key_exists($key,$this->adhoc))
-			return $this->adhoc[$key]['value'];
-		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
-	}
-
-	/**
-	*	Clear value of field
-	*	@return NULL
-	*	@param $key string
-	**/
-	function clear($key) {
-		if (array_key_exists($key,$this->adhoc))
-			unset($this->adhoc[$key]);
-	}
-
-	/**
-	*	Get PHP type equivalent of PDO constant
-	*	@return string
-	*	@param $pdo string
-	**/
-	function type($pdo) {
-		switch ($pdo) {
-			case \PDO::PARAM_NULL:
-				return 'unset';
-			case \PDO::PARAM_INT:
-				return 'int';
-			case \PDO::PARAM_BOOL:
-				return 'bool';
-			case \PDO::PARAM_STR:
-				return 'string';
-		}
-	}
-
-	/**
-	*	Convert array to mapper object
-	*	@return object
-	*	@param $row array
-	**/
-	protected function factory($row) {
-		$mapper=clone($this);
-		$mapper->reset();
-		foreach ($row as $key=>$val) {
-			if (array_key_exists($key,$this->fields))
-				$var='fields';
-			elseif (array_key_exists($key,$this->adhoc))
-				$var='adhoc';
-			else
-				continue;
-			$mapper->{$var}[$key]['value']=$val;
-			if ($var=='fields' && $mapper->{$var}[$key]['pkey'])
-				$mapper->{$var}[$key]['previous']=$val;
-		}
-		$mapper->query=array(clone($mapper));
-		if (isset($mapper->trigger['load']))
-			\Base::instance()->call($mapper->trigger['load'],$mapper);
-		return $mapper;
-	}
-
-	/**
-	*	Return fields of mapper object as an associative array
-	*	@return array
-	*	@param $obj object
-	**/
-	function cast($obj=NULL) {
-		if (!$obj)
-			$obj=$this;
-		return array_map(
-			function($row) {
-				return $row['value'];
-			},
-			$obj->fields+$obj->adhoc
-		);
-	}
-
-	/**
-	*	Build query string and execute
-	*	@return \DB\SQL\Mapper[]
-	*	@param $fields string
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
-		if (!$options)
-			$options=array();
-		$options+=array(
-			'group'=>NULL,
-			'order'=>NULL,
-			'limit'=>0,
-			'offset'=>0
-		);
-		$db=$this->db;
-		$sql='SELECT '.$fields.' FROM '.$this->table;
-		$args=array();
-		if ($filter) {
-			if (is_array($filter)) {
-				$args=isset($filter[1]) && is_array($filter[1])?
-					$filter[1]:
-					array_slice($filter,1,NULL,TRUE);
-				$args=is_array($args)?$args:array(1=>$args);
-				list($filter)=$filter;
-			}
-			$sql.=' WHERE '.$filter;
-		}
-		if ($options['group']) {
-			$sql.=' GROUP BY '.implode(',',array_map(
-				function($str) use($db) {
-					return preg_replace_callback(
-						'/\b(\w+)\h*(HAVING.+|$)/i',
-						function($parts) use($db) {
-							return $db->quotekey($parts[1]);
-						},
-						$str
-					);
-				},
-				explode(',',$options['group'])));
-		}
-		if ($options['order']) {
-			$sql.=' ORDER BY '.implode(',',array_map(
-				function($str) use($db) {
-					return preg_match('/^(\w+)(?:\h+(ASC|DESC))?\h*(?:,|$)/i',
-						$str,$parts)?
-						($db->quotekey($parts[1]).
-						(isset($parts[2])?(' '.$parts[2]):'')):$str;
-				},
-				explode(',',$options['order'])));
-		}
-		if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
-			($options['limit'] || $options['offset'])) {
-			$pkeys=array();
-			foreach ($this->fields as $key=>$field)
-				if ($field['pkey'])
-					$pkeys[]=$key;
-			$ofs=$options['offset']?(int)$options['offset']:0;
-			$lmt=$options['limit']?(int)$options['limit']:0;
-			if (strncmp($db->version(),'11',2)>=0) {
-				// SQL Server 2012
-				if (!$options['order'])
-					$sql.=' ORDER BY '.$db->quotekey($pkeys[0]);
-				$sql.=' OFFSET '.$ofs.' ROWS';
-				if ($lmt)
-					$sql.=' FETCH NEXT '.$lmt.' ROWS ONLY';
-			}
-			else {
-				// SQL Server 2008
-				$sql=str_replace('SELECT',
-					'SELECT '.
-					($lmt>0?'TOP '.($ofs+$lmt):'').' ROW_NUMBER() '.
-					'OVER (ORDER BY '.
-						$db->quotekey($pkeys[0]).') AS rnum,',$sql);
-				$sql='SELECT * FROM ('.$sql.') x WHERE rnum > '.($ofs);
-			}
-		}
-		else {
-			if ($options['limit'])
-				$sql.=' LIMIT '.(int)$options['limit'];
-			if ($options['offset'])
-				$sql.=' OFFSET '.(int)$options['offset'];
-		}
-		$result=$this->db->exec($sql,$args,$ttl);
-		$out=array();
-		foreach ($result as &$row) {
-			foreach ($row as $field=>&$val) {
-				if (array_key_exists($field,$this->fields)) {
-					if (!is_null($val) || !$this->fields[$field]['nullable'])
-						$val=$this->db->value(
-							$this->fields[$field]['pdo_type'],$val);
-				}
-				elseif (array_key_exists($field,$this->adhoc))
-					$this->adhoc[$field]['value']=$val;
-				unset($val);
-			}
-			$out[]=$this->factory($row);
-			unset($row);
-		}
-		return $out;
-	}
-
-	/**
-	*	Return records that match criteria
-	*	@return \DB\SQL\Mapper[]
-	*	@param $filter string|array
-	*	@param $options array
-	*	@param $ttl int
-	**/
-	function find($filter=NULL,array $options=NULL,$ttl=0) {
-		if (!$options)
-			$options=array();
-		$options+=array(
-			'group'=>NULL,
-			'order'=>NULL,
-			'limit'=>0,
-			'offset'=>0
-		);
-		$adhoc='';
-		foreach ($this->adhoc as $key=>$field)
-			$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
-		return $this->select(
-			($options['group'] && !preg_match('/mysql|sqlite/',$this->engine)?
-				$options['group']:
-				implode(',',array_map(array($this->db,'quotekey'),
-					array_keys($this->fields)))).$adhoc,$filter,$options,$ttl);
-	}
-
-	/**
-	*	Count records that match criteria
-	*	@return int
-	*	@param $filter string|array
-	*	@param $ttl int
-	**/
-	function count($filter=NULL,$ttl=0) {
-		$sql='SELECT COUNT(*) AS '.
-			$this->db->quotekey('rows').' FROM '.$this->table;
-		$args=array();
-		if ($filter) {
-			if (is_array($filter)) {
-				$args=isset($filter[1]) && is_array($filter[1])?
-					$filter[1]:
-					array_slice($filter,1,NULL,TRUE);
-				$args=is_array($args)?$args:array(1=>$args);
-				list($filter)=$filter;
-			}
-			$sql.=' WHERE '.$filter;
-		}
-		$result=$this->db->exec($sql,$args,$ttl);
-		return $result[0]['rows'];
-	}
-
-	/**
-	*	Return record at specified offset using same criteria as
-	*	previous load() call and make it active
-	*	@return array
-	*	@param $ofs int
-	**/
-	function skip($ofs=1) {
-		$out=parent::skip($ofs);
-		$dry=$this->dry();
-		foreach ($this->fields as $key=>&$field) {
-			$field['value']=$dry?NULL:$out->fields[$key]['value'];
-			$field['changed']=FALSE;
-			if ($field['pkey'])
-				$field['previous']=$dry?NULL:$out->fields[$key]['value'];
-			unset($field);
-		}
-		foreach ($this->adhoc as $key=>&$field) {
-			$field['value']=$dry?NULL:$out->adhoc[$key]['value'];
-			unset($field);
-		}
-		if (isset($this->trigger['load']))
-			\Base::instance()->call($this->trigger['load'],$this);
-		return $out;
-	}
-
-	/**
-	*	Insert new record
-	*	@return object
-	**/
-	function insert() {
-		$args=array();
-		$actr=0;
-		$nctr=0;
-		$fields='';
-		$values='';
-		$filter='';
-		$pkeys=array();
-		$nkeys=array();
-		$ckeys=array();
-		$inc=NULL;
-		foreach ($this->fields as $key=>$field)
-			if ($field['pkey'])
-				$pkeys[$key]=$field['previous'];
-		if (isset($this->trigger['beforeinsert']) &&
-			\Base::instance()->call($this->trigger['beforeinsert'],
-				array($this,$pkeys))===FALSE)
-			return $this;
-		foreach ($this->fields as $key=>&$field) {
-			if ($field['pkey']) {
-				$field['previous']=$field['value'];
-				if (!$inc && $field['pdo_type']==\PDO::PARAM_INT &&
-					empty($field['value']) && !$field['nullable'])
-					$inc=$key;
-				$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
-				$nkeys[$nctr+1]=array($field['value'],$field['pdo_type']);
-				$nctr++;
-			}
-			if ($field['changed'] && $key!=$inc) {
-				$fields.=($actr?',':'').$this->db->quotekey($key);
-				$values.=($actr?',':'').'?';
-				$args[$actr+1]=array($field['value'],$field['pdo_type']);
-				$actr++;
-				$ckeys[]=$key;
-			}
-			$field['changed']=FALSE;
-			unset($field);
-		}
-		if ($fields) {
-			$this->db->exec(
-				(preg_match('/mssql|dblib|sqlsrv/',$this->engine) &&
-				array_intersect(array_keys($pkeys),$ckeys)?
-					'SET IDENTITY_INSERT '.$this->table.' ON;':'').
-				'INSERT INTO '.$this->table.' ('.$fields.') '.
-				'VALUES ('.$values.')',$args
-			);
-			$seq=NULL;
-			if ($this->engine=='pgsql') {
-				$names=array_keys($pkeys);
-				$seq=$this->source.'_'.end($names).'_seq';
-			}
-			if ($this->engine!='oci')
-				$this->_id=$this->db->lastinsertid($seq);
-			// Reload to obtain default and auto-increment field values
-			$this->load($inc?
-				array($inc.'=?',$this->db->value(
-					$this->fields[$inc]['pdo_type'],$this->_id)):
-				array($filter,$nkeys));
-			if (isset($this->trigger['afterinsert']))
-				\Base::instance()->call($this->trigger['afterinsert'],
-					array($this,$pkeys));
-		}
-		return $this;
-	}
-
-	/**
-	*	Update current record
-	*	@return object
-	**/
-	function update() {
-		$args=array();
-		$ctr=0;
-		$pairs='';
-		$filter='';
-		$pkeys=array();
-		foreach ($this->fields as $key=>$field)
-			if ($field['pkey'])
-				$pkeys[$key]=$field['previous'];
-		if (isset($this->trigger['beforeupdate']) &&
-			\Base::instance()->call($this->trigger['beforeupdate'],
-				array($this,$pkeys))===FALSE)
-			return $this;
-		foreach ($this->fields as $key=>$field)
-			if ($field['changed']) {
-				$pairs.=($pairs?',':'').$this->db->quotekey($key).'=?';
-				$args[$ctr+1]=array($field['value'],$field['pdo_type']);
-				$ctr++;
-			}
-		foreach ($this->fields as $key=>$field)
-			if ($field['pkey']) {
-				$filter.=($filter?' AND ':' WHERE ').
-					$this->db->quotekey($key).'=?';
-				$args[$ctr+1]=array($field['previous'],$field['pdo_type']);
-				$ctr++;
-			}
-		if ($pairs) {
-			$sql='UPDATE '.$this->table.' SET '.$pairs.$filter;
-			$this->db->exec($sql,$args);
-			if (isset($this->trigger['afterupdate']))
-				\Base::instance()->call($this->trigger['afterupdate'],
-					array($this,$pkeys));
-		}
-		return $this;
-	}
-
-	/**
-	*	Delete current record
-	*	@return int
-	*	@param $filter string|array
-	**/
-	function erase($filter=NULL) {
-		if ($filter) {
-			$args=array();
-			if (is_array($filter)) {
-				$args=isset($filter[1]) && is_array($filter[1])?
-					$filter[1]:
-					array_slice($filter,1,NULL,TRUE);
-				$args=is_array($args)?$args:array(1=>$args);
-				list($filter)=$filter;
-			}
-			return $this->db->
-				exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
-		}
-		$args=array();
-		$ctr=0;
-		$filter='';
-		$pkeys=array();
-		foreach ($this->fields as $key=>&$field) {
-			if ($field['pkey']) {
-				$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
-				$args[$ctr+1]=array($field['previous'],$field['pdo_type']);
-				$pkeys[$key]=$field['previous'];
-				$ctr++;
-			}
-			$field['value']=NULL;
-			$field['changed']=(bool)$field['default'];
-			if ($field['pkey'])
-				$field['previous']=NULL;
-			unset($field);
-		}
-		foreach ($this->adhoc as &$field) {
-			$field['value']=NULL;
-			unset($field);
-		}
-		parent::erase();
-		if (isset($this->trigger['beforeerase']) &&
-			\Base::instance()->call($this->trigger['beforeerase'],
-				array($this,$pkeys))===FALSE)
-			return 0;
-		$out=$this->db->
-			exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
-		if (isset($this->trigger['aftererase']))
-			\Base::instance()->call($this->trigger['aftererase'],
-				array($this,$pkeys));
-		return $out;
-	}
-
-	/**
-	*	Reset cursor
-	*	@return NULL
-	**/
-	function reset() {
-		foreach ($this->fields as &$field) {
-			$field['value']=NULL;
-			$field['changed']=FALSE;
-			if ($field['pkey'])
-				$field['previous']=NULL;
-			unset($field);
-		}
-		foreach ($this->adhoc as &$field) {
-			$field['value']=NULL;
-			unset($field);
-		}
-		parent::reset();
-	}
-
-	/**
-	*	Hydrate mapper object using hive array variable
-	*	@return NULL
-	*	@param $var array|string
-	*	@param $func callback
-	**/
-	function copyfrom($var,$func=NULL) {
-		if (is_string($var))
-			$var=\Base::instance()->get($var);
-		if ($func)
-			$var=call_user_func($func,$var);
-		foreach ($var as $key=>$val)
-			if (in_array($key,array_keys($this->fields))) {
-				$field=&$this->fields[$key];
-				if ($field['value']!==$val) {
-					$field['value']=$val;
-					$field['changed']=TRUE;
-				}
-				unset($field);
-			}
-	}
-
-	/**
-	*	Populate hive array variable with mapper fields
-	*	@return NULL
-	*	@param $key string
-	**/
-	function copyto($key) {
-		$var=&\Base::instance()->ref($key);
-		foreach ($this->fields+$this->adhoc as $key=>$field)
-			$var[$key]=$field['value'];
-	}
-
-	/**
-	*	Return schema and, if the first argument is provided, update it
-	*	@return array
-	*	@param $fields NULL|array
-	**/
-	function schema($fields=null) {
-		if ($fields)
-			$this->fields = $fields;
-		return $this->fields;
-	}
-
-	/**
-	*	Return field names
-	*	@return array
-	*	@param $adhoc bool
-	**/
-	function fields($adhoc=TRUE) {
-		return array_keys($this->fields+($adhoc?$this->adhoc:array()));
-	}
-
-	/**
-	*	Return TRUE if field is not nullable
-	*	@return bool
-	*	@param $field string
-	**/
-	function required($field) {
-		return isset($this->fields[$field]) &&
-			!$this->fields[$field]['nullable'];
-	}
-
-	/**
-	*	Retrieve external iterator for fields
-	*	@return object
-	**/
-	function getiterator() {
-		return new \ArrayIterator($this->cast());
-	}
-
-	/**
-	*	Instantiate class
-	*	@param $db object
-	*	@param $table string
-	*	@param $fields array|string
-	*	@param $ttl int
-	**/
-	function __construct(\DB\SQL $db,$table,$fields=NULL,$ttl=60) {
-		$this->db=$db;
-		$this->engine=$db->driver();
-		if ($this->engine=='oci')
-			$table=strtoupper($table);
-		$this->source=$table;
-		$this->table=$this->db->quotekey($table);
-		$this->fields=$db->schema($table,$fields,$ttl);
-		$this->reset();
-	}
-
-}

+ 0 - 203
frameworks/PHP/fat-free/src/db/sql/session.php

@@ -1,203 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-namespace DB\SQL;
-
-//! SQL-managed session handler
-class Session extends Mapper {
-
-	protected
-		//! Session ID
-		$sid;
-
-	/**
-	*	Open session
-	*	@return TRUE
-	*	@param $path string
-	*	@param $name string
-	**/
-	function open($path,$name) {
-		return TRUE;
-	}
-
-	/**
-	*	Close session
-	*	@return TRUE
-	**/
-	function close() {
-		return TRUE;
-	}
-
-	/**
-	*	Return session data in serialized format
-	*	@return string|FALSE
-	*	@param $id string
-	**/
-	function read($id) {
-		if ($id!=$this->sid)
-			$this->load(array('session_id=?',$this->sid=$id));
-		return $this->dry()?FALSE:$this->get('data');
-	}
-
-	/**
-	*	Write session data
-	*	@return TRUE
-	*	@param $id string
-	*	@param $data string
-	**/
-	function write($id,$data) {
-		$fw=\Base::instance();
-		$sent=headers_sent();
-		$headers=$fw->get('HEADERS');
-		if ($id!=$this->sid)
-			$this->load(array('session_id=?',$this->sid=$id));
-		$csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
-			$fw->hash(mt_rand());
-		$this->set('session_id',$id);
-		$this->set('data',$data);
-		$this->set('csrf',$sent?$this->csrf():$csrf);
-		$this->set('ip',$fw->get('IP'));
-		$this->set('agent',
-			isset($headers['User-Agent'])?$headers['User-Agent']:'');
-		$this->set('stamp',time());
-		$this->save();
-		return TRUE;
-	}
-
-	/**
-	*	Destroy session
-	*	@return TRUE
-	*	@param $id string
-	**/
-	function destroy($id) {
-		$this->erase(array('session_id=?',$id));
-		setcookie(session_name(),'',strtotime('-1 year'));
-		unset($_COOKIE[session_name()]);
-		header_remove('Set-Cookie');
-		return TRUE;
-	}
-
-	/**
-	*	Garbage collector
-	*	@return TRUE
-	*	@param $max int
-	**/
-	function cleanup($max) {
-		$this->erase(array('stamp+?<?',$max,time()));
-		return TRUE;
-	}
-
-	/**
-	*	Return anti-CSRF token
-	*	@return string|FALSE
-	**/
-	function csrf() {
-		return $this->dry()?FALSE:$this->get('csrf');
-	}
-
-	/**
-	*	Return IP address
-	*	@return string|FALSE
-	**/
-	function ip() {
-		return $this->dry()?FALSE:$this->get('ip');
-	}
-
-	/**
-	*	Return Unix timestamp
-	*	@return string|FALSE
-	**/
-	function stamp() {
-		return $this->dry()?FALSE:$this->get('stamp');
-	}
-
-	/**
-	*	Return HTTP user agent
-	*	@return string|FALSE
-	**/
-	function agent() {
-		return $this->dry()?FALSE:$this->get('agent');
-	}
-
-	/**
-	*	Instantiate class
-	*	@param $db object
-	*	@param $table string
-	*	@param $force bool
-	*	@param $onsuspect callback
-	**/
-	function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL) {
-		if ($force) {
-			$eol="\n";
-			$tab="\t";
-			$db->exec(
-				(preg_match('/mssql|sqlsrv|sybase/',$db->driver())?
-					('IF NOT EXISTS (SELECT * FROM sysobjects WHERE '.
-						'name='.$db->quote($table).' AND xtype=\'U\') '.
-						'CREATE TABLE dbo.'):
-					('CREATE TABLE IF NOT EXISTS '.
-						((($name=$db->name())&&$db->driver()!='pgsql')?
-							($name.'.'):''))).
-				$table.' ('.$eol.
-					$tab.$db->quotekey('session_id').' VARCHAR(40),'.$eol.
-					$tab.$db->quotekey('data').' TEXT,'.$eol.
-					$tab.$db->quotekey('csrf').' TEXT,'.$eol.
-					$tab.$db->quotekey('ip').' VARCHAR(40),'.$eol.
-					$tab.$db->quotekey('agent').' VARCHAR(255),'.$eol.
-					$tab.$db->quotekey('stamp').' INTEGER,'.$eol.
-					$tab.'PRIMARY KEY ('.$db->quotekey('session_id').')'.$eol.
-				');'
-			);
-		}
-		parent::__construct($db,$table);
-		session_set_save_handler(
-			array($this,'open'),
-			array($this,'close'),
-			array($this,'read'),
-			array($this,'write'),
-			array($this,'destroy'),
-			array($this,'cleanup')
-		);
-		register_shutdown_function('session_commit');
-		@session_start();
-		$fw=\Base::instance();
-		$headers=$fw->get('HEADERS');
-		if (($ip=$this->ip()) && $ip!=$fw->get('IP') ||
-			($agent=$this->agent()) &&
-			(!isset($headers['User-Agent']) ||
-				$agent!=$headers['User-Agent'])) {
-			if (isset($onsuspect))
-				$fw->call($onsuspect,array($this));
-			else {
-				session_destroy();
-				$fw->error(403);
-			}
-		}
-		$csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
-			$fw->hash(mt_rand());
-		if ($this->load(array('session_id=?',$this->sid=session_id()))) {
-			$this->set('csrf',$csrf);
-			$this->save();
-		}
-	}
-
-}

+ 0 - 139
frameworks/PHP/fat-free/src/magic.php

@@ -1,139 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-//! PHP magic wrapper
-abstract class Magic implements ArrayAccess {
-
-	/**
-	*	Return TRUE if key is not empty
-	*	@return bool
-	*	@param $key string
-	**/
-	abstract function exists($key);
-
-	/**
-	*	Bind value to key
-	*	@return mixed
-	*	@param $key string
-	*	@param $val mixed
-	**/
-	abstract function set($key,$val);
-
-	/**
-	*	Retrieve contents of key
-	*	@return mixed
-	*	@param $key string
-	**/
-	abstract function &get($key);
-
-	/**
-	*	Unset key
-	*	@return NULL
-	*	@param $key string
-	**/
-	abstract function clear($key);
-
-	/**
-	*	Convenience method for checking property value
-	*	@return mixed
-	*	@param $key string
-	**/
-	function offsetexists($key) {
-		return Base::instance()->visible($this,$key)?
-			isset($this->$key):$this->exists($key);
-	}
-
-	/**
-	*	Convenience method for assigning property value
-	*	@return mixed
-	*	@param $key string
-	*	@param $val scalar
-	**/
-	function offsetset($key,$val) {
-		return Base::instance()->visible($this,$key)?
-			($this->key=$val):$this->set($key,$val);
-	}
-
-	/**
-	*	Convenience method for retrieving property value
-	*	@return mixed
-	*	@param $key string
-	**/
-	function &offsetget($key) {
-		if (Base::instance()->visible($this,$key))
-			$val=&$this->$key;
-		else
-			$val=&$this->get($key);
-		return $val;
-	}
-
-	/**
-	*	Convenience method for removing property value
-	*	@return NULL
-	*	@param $key string
-	**/
-	function offsetunset($key) {
-		if (Base::instance()->visible($this,$key))
-			unset($this->$key);
-		else
-			$this->clear($key);
-	}
-
-	/**
-	*	Alias for offsetexists()
-	*	@return mixed
-	*	@param $key string
-	**/
-	function __isset($key) {
-		return $this->offsetexists($key);
-	}
-
-	/**
-	*	Alias for offsetset()
-	*	@return mixed
-	*	@param $key string
-	*	@param $val scalar
-	**/
-	function __set($key,$val) {
-		return $this->offsetset($key,$val);
-	}
-
-	/**
-	*	Alias for offsetget()
-	*	@return mixed
-	*	@param $key string
-	**/
-	function &__get($key) {
-		$val=&$this->offsetget($key);
-		return $val;
-	}
-
-	/**
-	*	Alias for offsetunset()
-	*	@return NULL
-	*	@param $key string
-	**/
-	function __unset($key) {
-		$this->offsetunset($key);
-	}
-
-}

+ 0 - 108
frameworks/PHP/fat-free/src/matrix.php

@@ -1,108 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-//! Generic array utilities
-class Matrix extends Prefab {
-
-	/**
-	*	Retrieve values from a specified column of a multi-dimensional
-	*	array variable
-	*	@return array
-	*	@param $var array
-	*	@param $col mixed
-	**/
-	function pick(array $var,$col) {
-		return array_map(
-			function($row) use($col) {
-				return $row[$col];
-			},
-			$var
-		);
-	}
-
-	/**
-	*	Rotate a two-dimensional array variable
-	*	@return NULL
-	*	@param $var array
-	**/
-	function transpose(array &$var) {
-		$out=array();
-		foreach ($var as $keyx=>$cols)
-			foreach ($cols as $keyy=>$valy)
-				$out[$keyy][$keyx]=$valy;
-		$var=$out;
-	}
-
-	/**
-	*	Sort a multi-dimensional array variable on a specified column
-	*	@return bool
-	*	@param $var array
-	*	@param $col mixed
-	*	@param $order int
-	**/
-	function sort(array &$var,$col,$order=SORT_ASC) {
-		uasort(
-			$var,
-			function($val1,$val2) use($col,$order) {
-				list($v1,$v2)=array($val1[$col],$val2[$col]);
-				$out=is_numeric($v1) && is_numeric($v2)?
-					Base::instance()->sign($v1-$v2):strcmp($v1,$v2);
-				if ($order==SORT_DESC)
-					$out=-$out;
-				return $out;
-			}
-		);
-		$var=array_values($var);
-	}
-
-	/**
-	*	Change the key of a two-dimensional array element
-	*	@return NULL
-	*	@param $var array
-	*	@param $old string
-	*	@param $new string
-	**/
-	function changekey(array &$var,$old,$new) {
-		$keys=array_keys($var);
-		$vals=array_values($var);
-		$keys[array_search($old,$keys)]=$new;
-		$var=array_combine($keys,$vals);
-	}
-
-	/**
-	*	Return month calendar of specified date, with optional setting for
-	*	first day of week (0 for Sunday)
-	*	@return array
-	*	@param $date string
-	*	@param $first int
-	**/
-	function calendar($date='now',$first=0) {
-		$parts=getdate(strtotime($date));
-		$days=cal_days_in_month(CAL_GREGORIAN,$parts['mon'],$parts['year']);
-		$ref=date('w',strtotime(date('Y-m',$parts[0]).'-01'))+(7-$first)%7;
-		$out=array();
-		for ($i=0;$i<$days;$i++)
-			$out[floor(($ref+$i)/7)][($ref+$i)%7]=$i+1;
-		return $out;
-	}
-
-}

+ 0 - 357
frameworks/PHP/fat-free/src/template.php

@@ -1,357 +0,0 @@
-<?php
-
-/*
-
-	Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
-
-	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
-
-	This is free software: you can redistribute it and/or modify it under the
-	terms of the GNU General Public License as published by the Free Software
-	Foundation, either version 3 of the License, or later.
-
-	Fat-Free Framework is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-	General Public License for more details.
-
-	You should have received a copy of the GNU General Public License along
-	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-//! XML-style template engine
-class Template extends Preview {
-
-	//@{ Error messages
-	const
-		E_Method='Call to undefined method %s()';
-	//@}
-
-	protected
-		//! Template tags
-		$tags,
-		//! Custom tag handlers
-		$custom=array();
-
-	/**
-	*	Template -set- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _set(array $node) {
-		$out='';
-		foreach ($node['@attrib'] as $key=>$val)
-			$out.='$'.$key.'='.
-				(preg_match('/\{\{(.+?)\}\}/',$val)?
-					$this->token($val):
-					Base::instance()->stringify($val)).'; ';
-		return '<?php '.$out.'?>';
-	}
-
-	/**
-	*	Template -include- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _include(array $node) {
-		$attrib=$node['@attrib'];
-		$hive=isset($attrib['with']) &&
-			($attrib['with']=$this->token($attrib['with'])) &&
-			preg_match_all('/(\w+)\h*=\h*(.+?)(?=,|$)/',
-				$attrib['with'],$pairs,PREG_SET_ORDER)?
-					'array('.implode(',',
-						array_map(function($pair) {
-							return '\''.$pair[1].'\'=>'.
-								(preg_match('/^\'.*\'$/',$pair[2]) ||
-									preg_match('/\$/',$pair[2])?
-									$pair[2]:
-									\Base::instance()->stringify($pair[2]));
-						},$pairs)).')+get_defined_vars()':
-					'get_defined_vars()';
-		return
-			'<?php '.(isset($attrib['if'])?
-				('if ('.$this->token($attrib['if']).') '):'').
-				('echo $this->render('.
-					(preg_match('/^\{\{(.+?)\}\}$/',$attrib['href'])?
-						$this->token($attrib['href']):
-						Base::instance()->stringify($attrib['href'])).','.
-					'$this->mime,'.$hive.'); ?>');
-	}
-
-	/**
-	*	Template -exclude- tag handler
-	*	@return string
-	**/
-	protected function _exclude() {
-		return '';
-	}
-
-	/**
-	*	Template -ignore- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _ignore(array $node) {
-		return $node[0];
-	}
-
-	/**
-	*	Template -loop- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _loop(array $node) {
-		$attrib=$node['@attrib'];
-		unset($node['@attrib']);
-		return
-			'<?php for ('.
-				$this->token($attrib['from']).';'.
-				$this->token($attrib['to']).';'.
-				$this->token($attrib['step']).'): ?>'.
-				$this->build($node).
-			'<?php endfor; ?>';
-	}
-
-	/**
-	*	Template -repeat- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _repeat(array $node) {
-		$attrib=$node['@attrib'];
-		unset($node['@attrib']);
-		return
-			'<?php '.
-				(isset($attrib['counter'])?
-					(($ctr=$this->token($attrib['counter'])).'=0; '):'').
-				'foreach (('.
-				$this->token($attrib['group']).'?:array()) as '.
-				(isset($attrib['key'])?
-					($this->token($attrib['key']).'=>'):'').
-				$this->token($attrib['value']).'):'.
-				(isset($ctr)?(' '.$ctr.'++;'):'').' ?>'.
-				$this->build($node).
-			'<?php endforeach; ?>';
-	}
-
-	/**
-	*	Template -check- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _check(array $node) {
-		$attrib=$node['@attrib'];
-		unset($node['@attrib']);
-		// Grab <true> and <false> blocks
-		foreach ($node as $pos=>$block)
-			if (isset($block['true']))
-				$true=array($pos,$block);
-			elseif (isset($block['false']))
-				$false=array($pos,$block);
-		if (isset($true,$false) && $true[0]>$false[0])
-			// Reverse <true> and <false> blocks
-			list($node[$true[0]],$node[$false[0]])=array($false[1],$true[1]);
-		return
-			'<?php if ('.$this->token($attrib['if']).'): ?>'.
-				$this->build($node).
-			'<?php endif; ?>';
-	}
-
-	/**
-	*	Template -true- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _true(array $node) {
-		return $this->build($node);
-	}
-
-	/**
-	*	Template -false- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _false(array $node) {
-		return '<?php else: ?>'.$this->build($node);
-	}
-
-	/**
-	*	Template -switch- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _switch(array $node) {
-		$attrib=$node['@attrib'];
-		unset($node['@attrib']);
-		foreach ($node as $pos=>$block)
-			if (is_string($block) && !preg_replace('/\s+/','',$block))
-				unset($node[$pos]);
-		return
-			'<?php switch ('.$this->token($attrib['expr']).'): ?>'.
-				$this->build($node).
-			'<?php endswitch; ?>';
-	}
-
-	/**
-	*	Template -case- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _case(array $node) {
-		$attrib=$node['@attrib'];
-		unset($node['@attrib']);
-		return
-			'<?php case '.(preg_match('/\{\{(.+?)\}\}/',$attrib['value'])?
-				$this->token($attrib['value']):
-				Base::instance()->stringify($attrib['value'])).': ?>'.
-				$this->build($node).
-			'<?php '.(isset($attrib['break'])?
-				'if ('.$this->token($attrib['break']).') ':'').
-				'break; ?>';
-	}
-
-	/**
-	*	Template -default- tag handler
-	*	@return string
-	*	@param $node array
-	**/
-	protected function _default(array $node) {
-		return
-			'<?php default: ?>'.
-				$this->build($node).
-			'<?php break; ?>';
-	}
-
-	/**
-	*	Assemble markup
-	*	@return string
-	*	@param $node array|string
-	**/
-	protected function build($node) {
-		if (is_string($node))
-			return parent::build($node);
-		$out='';
-		foreach ($node as $key=>$val)
-			$out.=is_int($key)?$this->build($val):$this->{'_'.$key}($val);
-		return $out;
-	}
-
-	/**
-	*	Extend template with custom tag
-	*	@return NULL
-	*	@param $tag string
-	*	@param $func callback
-	**/
-	function extend($tag,$func) {
-		$this->tags.='|'.$tag;
-		$this->custom['_'.$tag]=$func;
-	}
-
-	/**
-	*	Call custom tag handler
-	*	@return string|FALSE
-	*	@param $func callback
-	*	@param $args array
-	**/
-	function __call($func,array $args) {
-		if ($func[0]=='_')
-			return call_user_func_array($this->custom[$func],$args);
-		if (method_exists($this,$func))
-			return call_user_func_array(array($this,$func),$args);
-		user_error(sprintf(self::E_Method,$func),E_USER_ERROR);
-	}
-
-	/**
-	*	Parse string for template directives and tokens
-	*	@return string|array
-	*	@param $text string
-	**/
-	function parse($text) {
-		// Build tree structure
-		for ($ptr=0,$len=strlen($text),$tree=array(),$node=&$tree,
-			$stack=array(),$depth=0,$tmp='';$ptr<$len;)
-			if (preg_match('/^<(\/?)(?:F3:)?'.
-				'('.$this->tags.')\b((?:\h+[\w-]+'.
-				'(?:\h*=\h*(?:"(?:.+?)"|\'(?:.+?)\'))?|'.
-				'\h*\{\{.+?\}\})*)\h*(\/?)>/is',
-				substr($text,$ptr),$match)) {
-				if (strlen($tmp))
-					$node[]=$tmp;
-				// Element node
-				if ($match[1]) {
-					// Find matching start tag
-					$save=$depth;
-					$found=FALSE;
-					while ($depth>0) {
-						$depth--;
-						foreach ($stack[$depth] as $item)
-							if (is_array($item) && isset($item[$match[2]])) {
-								// Start tag found
-								$found=TRUE;
-								break 2;
-							}
-					}
-					if (!$found)
-						// Unbalanced tag
-						$depth=$save;
-					$node=&$stack[$depth];
-				}
-				else {
-					// Start tag
-					$stack[$depth]=&$node;
-					$node=&$node[][$match[2]];
-					if ($match[3]) {
-						// Process attributes
-						preg_match_all(
-							'/(?:\b([\w-]+)\h*'.
-							'(?:=\h*(?:"(.*?)"|\'(.*?)\'))?|'.
-							'(\{\{.+?\}\}))/s',
-							$match[3],$attr,PREG_SET_ORDER);
-						foreach ($attr as $kv)
-							if (isset($kv[4]))
-								$node['@attrib'][]=$kv[4];
-							else
-								$node['@attrib'][$kv[1]]=
-									(isset($kv[2]) && $kv[2]!==''?
-										$kv[2]:
-										(isset($kv[3]) && $kv[3]!==''?
-											$kv[3]:NULL));
-					}
-					if ($match[4])
-						// Empty tag
-						$node=&$stack[$depth];
-					else
-						$depth++;
-				}
-				$tmp='';
-				$ptr+=strlen($match[0]);
-			}
-			else {
-				// Text node
-				$tmp.=substr($text,$ptr,1);
-				$ptr++;
-			}
-		if (strlen($tmp))
-			// Append trailing text
-			$node[]=$tmp;
-		// Break references
-		unset($node);
-		unset($stack);
-		return $tree;
-	}
-
-	/**
-	*	Class constructor
-	*	return object
-	**/
-	function __construct() {
-		$ref=new ReflectionClass(__CLASS__);
-		$this->tags='';
-		foreach ($ref->getmethods() as $method)
-			if (preg_match('/^_(?=[[:alpha:]])/',$method->name))
-				$this->tags.=(strlen($this->tags)?'|':'').
-					substr($method->name,1);
-	}
-
-}