Compare commits

...

179 Commits

Author SHA1 Message Date
Sven Slootweg 87a0cadb18 starts_with, notice backtraces, better database error reporting and return values 10 years ago
Sven Slootweg 37b404c2dd Make random_string cryptographically secure by default 10 years ago
Sven Slootweg 4a97d7b0ee Correctly return HTTP 500 on uncaught exception pages, and also display tracebacks for nested exceptions. 10 years ago
Sven Slootweg 7076e8d0fa Fixes for CSRF protection and FormHandler (including calling CSRF validation in FormHandler by default), Switch_/Case_ statements for FormHandler, custom FormHandler error messages for standard validators 10 years ago
Sven Slootweg 4ffa38e442 Add custom error message map functionality to form handler 10 years ago
Sven Slootweg c0b077dc9f Templater and form handler updates 10 years ago
Sven Slootweg 2db9f1bb7f Slight change in GetGroupedValues API 10 years ago
Sven Slootweg a6c87d62a5 WTF? 10 years ago
Sven Slootweg 7c87c4716e Add regex validator 10 years ago
Sven Slootweg 996054f69f Error handling and a bunch of validators 10 years ago
Sven Slootweg 24e018330c Implement AbortIfErrors statement for form validator 10 years ago
Sven Slootweg fc318180f7 Bugfix 10 years ago
Sven Slootweg 5c6445a023 Fix bug in operators 10 years ago
Sven Slootweg 57b11d4c58 I must be crazy. Reimplemented the whole thing with promises. 10 years ago
Sven Slootweg 081b8b6ec8 First bits of FormHandler 10 years ago
Sven Slootweg 35fcf51b53 Fixed a stupid comparison bug 11 years ago
Sven Slootweg 4423ff7a22 Merge branch 'bugfix/float-casting' into feature/orm-uuid 11 years ago
Sven Slootweg 1eb1427de4 Fix float casting bug 11 years ago
Sven Slootweg 7b3e986f65 Merge branch 'bugfix/int-overflow' into feature/orm-uuid 11 years ago
Sven Slootweg 4eacdbf612 Merge branch 'bugfix/int-overflow' into develop 11 years ago
Sven Slootweg 4e45466d9e Patch for integer overflow when auto-casting in a PDO query. 11 years ago
Sven Slootweg 898506fb0b Fix bug with UUIDs... 11 years ago
Sven Slootweg ea88a51e66 Also insert the Id when using UUIDs or other pre-specified IDs for new rows 11 years ago
Sven Slootweg 7cdb3507af Change ID handling, in preparation of UUID support 11 years ago
Sven Slootweg eaadadded0 Also use PDO parameterization for the row ID in UPDATE queries 11 years ago
Sven Slootweg 138bf9949e Update some comments and deprecation info 11 years ago
Sven Slootweg 6822c353c3 Add a 'forced' option to InsertIntoDatabase, to pre-fill all data instead of lazy-loading 11 years ago
Sven Slootweg 161dd60fd7 Be quiet about memcache connection failures 11 years ago
Sven Slootweg f6d92110dd Store a normalized version of the request method in the router object for external access 11 years ago
Sven Slootweg 03cf41519f Allow preset form values to be specified outside of the POST data. 11 years ago
Sven Slootweg c8566e4822 Properly handle errors during reading of configuration, and suppress PHP notices. 11 years ago
Sven Slootweg c7c2e252b9 Why was there even a limit on the amount of CSRF token insertions? 11 years ago
Sven Slootweg ce13fa4f29 Proper error reporting for database queries 11 years ago
Sven Slootweg ca82a9e0c8 Add basic autoloader 11 years ago
Sven Slootweg 6bfe6ccb4d Allow omission of the '0' when creating a blank object from a CPHP class. 11 years ago
Sven Slootweg c2ea87c4af Fix a notice 11 years ago
Sven Slootweg 68964d3022 Properly include database query error messages 11 years ago
Sven Slootweg bded80405e Add support for (cached) HTMLPurifier 11 years ago
Sven Slootweg 5f343edfcd Check strpos result explicitly, whoops 11 years ago
Sven Slootweg 238195c19a Add a patch for lighttpds strange behaviour on server.error-handler-404 routing. 11 years ago
Sven Slootweg 9ec934e8c7 The magic quotes undo should apply to GET variables as well. 11 years ago
Sven Slootweg 21f3cea18f Merge branch 'master' of git.cryto.net:projects/joepie91/cphp 11 years ago
Sven Slootweg a90ffa590d Throw a DatabaseException when an object is specified as a query parameter. 11 years ago
Sven Slootweg 58cfffd3d8 Throw a templater exception when a template attempts to access a non-existent key in a collection. 11 years ago
Sven Slootweg d42eecad3d Swap the class and procedural code in the PDO include, to avoid breakage 11 years ago
Sven Slootweg 539f54eac1 Resolve another notice 11 years ago
Sven Slootweg dbe5662a85 Resolve a bunch of notices and E_STRICT warnings 11 years ago
Sven Slootweg ca171e8275 Add HTTP 422 status code 11 years ago
Sven Slootweg 4b02357739 Include an error message in the exception when a database query fails. 11 years ago
Sven Slootweg bfadf150b4 Fix insertion of simplehtml, html, and nl2br data types 11 years ago
Sven Slootweg 2680c4538c Add support for restricting routes to specific methods 11 years ago
Sven Slootweg a80368e7c2 Only check the last JSON error if the function for it exists 11 years ago
Sven Slootweg 113404d563 Remove old mysql_ cache purging code, fix type inconsistency bug in cache purging, and fix a bug in the debugger (oh, the irony) 11 years ago
Sven Slootweg 3fbfad8656 Bypass cache on data reload, and fix a bug where a difference between column name and variable name would cause the clearing of old data in an object to fail 11 years ago
Sven Slootweg 7db7d5a60a Make CPHP deal properly with non-integer numeric values 11 years ago
Sven Slootweg 4aa82d0a86 Remove all mysql_ functionality and make insertion/updating use PDO instead. 11 years ago
Sven Slootweg da6ed16c34 Add TODO 11 years ago
Sven Slootweg 5729adac81 Add todo item 11 years ago
Sven Slootweg d2b511d9b3 Also add 'id' parameter override support for <select> elements 11 years ago
Sven Slootweg 85371b3394 Support NULL values for non-string database fields 11 years ago
Sven Slootweg 6e923629e0 Resolve conflicting variable name with 'value' parameter for input elements, and add support for 'id' parameter overrides 11 years ago
Sven Slootweg 249284e488 Always return an array from CreateFromQuery unless specifically the first result is requested 11 years ago
Sven Slootweg 4efacc178b Add a function for returning HTTP status code headers 11 years ago
Sven Slootweg 9aac04d33e Make the CreateFromQuery function return an array of results or one result if explicitly defined 11 years ago
Sven Slootweg 55de2ba21c Actually use the current class instead of the base class 11 years ago
Sven Slootweg ee8c40ebe1 Add function to spawn a new object from a custom query 11 years ago
Sven Slootweg 8e75ee9ee7 Add function to set a global templater variable 11 years ago
Sven Slootweg 75a5f0b7e7 Skip memcache retrieval if the expiry time is defined as 0 (after all, we clearly don't want stale data) 12 years ago
Sven Slootweg f54722d1a7 Get rid of magic_quotes nonsense 12 years ago
Sven Slootweg 52497d05b4 Implement variable hooks 12 years ago
Sven Slootweg c7ee85eab1 Fix nested subconstruct bugs and implement more debugging 12 years ago
Sven Slootweg 442a180f1e Implement a step-by-step debugger 12 years ago
Sven Slootweg a56c06a76e Fix parsing bug for constructs with subconstructs 12 years ago
Sven Slootweg 9d85fb8bc9 Make the CachedQuery function throw an exception when a query fails, instead of just returning null 12 years ago
Sven Slootweg 061516281a Implement select and option elements 12 years ago
Sven Slootweg 38112ba6e3 Fix bug with broken variable 12 years ago
Sven Slootweg aa6bb1fc85 Fix bug in fetching stand-alone variables and handle input values properly. 12 years ago
Sven Slootweg a55ce3778d Match all input element properties and properly deal with type attributes 12 years ago
Sven Slootweg fa8aa24e1b The form builder is retired :) 12 years ago
Sven Slootweg 29ea38ebc9 Remove excessive whitespace 12 years ago
Sven Slootweg db4d084163 Get rid of some stuff we don't need anymore 12 years ago
Sven Slootweg 5212ae353e Include field name in generated input elements 12 years ago
Sven Slootweg 3bf5d768cb Rewrite templater to support elseif/else constructs, input element generator, and extensible syntax. Also removed deprecated legacy code. 12 years ago
Sven Slootweg 533148edb1 Throw a RouterException if no suitable route was found 12 years ago
Sven Slootweg c62d6a9cec Deprecate the Export function 12 years ago
Sven Slootweg cb050ebdca Make code parameter optional when creating a new TemplateException 12 years ago
Sven Slootweg 46ea50e6b0 Store the authentication status internally in the router so that it can be used outside the included file 12 years ago
Sven Slootweg 9d08020a8d Use the new template parser for the errorhandler component 12 years ago
Sven Slootweg 4c6f46efe6 Also support global template variables in the new template parser 12 years ago
Sven Slootweg 18cd2f8066 Fix some PDO implementation stuff 12 years ago
Sven Slootweg 5417ccd5f6 Add a 'replace last occurrence' function 12 years ago
Sven Slootweg 95f9d81288 Fix parsing bug where characters after an opening curly brace were omitted in the output. 12 years ago
Sven Slootweg 7b4b4231db Make the PDO object cast nulls to empty strings to work around PHP retardedness 12 years ago
Sven Slootweg f14f916017 Fix mysql_ PDO wrapper 12 years ago
Sven Slootweg d80a4e487b Add redirect and ceil_precision functions 12 years ago
Sven Slootweg c59aad3be6 Fix horribly broken implementation of the PDO mysql_ abstraction - this REALLY needs to be done properly. 12 years ago
Sven Slootweg 224769ebb4 Fix yet another bug 12 years ago
Sven Slootweg d3d094a474 Fix bug in PDO mysql_ abstraction 12 years ago
Sven Slootweg 98f2145938 Fix a bug that would make database insertion break when using PDO 12 years ago
Sven Slootweg abb0712c97 Add no-expiry option to cached query functions, and fix cache invalidation bug 12 years ago
Sven Slootweg 4385243b9c Remove old config files, implement PDO shim, and implement better time handling 12 years ago
Sven Slootweg 4bbeffbd57 Create a new stdClass object before assigning properties 12 years ago
Sven Slootweg cc25349d30 Implement PDO in object retrieval 12 years ago
Sven Slootweg c53ffd9afb Make the CachedQuery function return null or false when an error condition occurs 12 years ago
Sven Slootweg d6ddb80b04 Make parameters optional and implement PDO properly 12 years ago
Sven Slootweg b3218ed1c3 Fix bug where class map in configuration is not recognized due to variable scope 12 years ago
Sven Slootweg 0dd070a809 Only retrieve query results as an associative array, omitting the numeric index 12 years ago
Sven Slootweg 1f45289ea3 Implement cacheable PDO object 12 years ago
Sven Slootweg c6b00de70b Fix localization code to actually use the parsed configuration file instead of relying on hardcoded configuration variables 12 years ago
Sven Slootweg 81fd2500ea Check if the strings array is actually a valid array before attempting to use it. 12 years ago
Sven Slootweg 63bb67a108 Also include locale handling code 12 years ago
Sven Slootweg 855d39c038 Fix spacing on timezone list 12 years ago
Sven Slootweg 52b6216054 Use a configuration parser instead of hardcoded configuration variables 12 years ago
Sven Slootweg 2e5590ddeb Add CSRF protection mechanism 12 years ago
Sven Slootweg 1a07c64d17 Remove PHP closing tags 12 years ago
Sven Slootweg 956933fe9b Add ends_with function 12 years ago
Sven Slootweg 2a629d63d3 Return null on query errors, instead of false 12 years ago
Sven Slootweg 6dbbfb4ebd Add correct null handling to templater 12 years ago
Sven Slootweg 5a3bacf2e7 Make the pagination function more compact, using less temporary variables. 12 years ago
Sven Slootweg a5f2e14a6d Add pagination calculation function. 12 years ago
Sven Slootweg 8a46f5d630 Make database insertion play nice with autoloading. 12 years ago
Sven Slootweg 4caf68e6ff Properly refresh data when using autoloading by unsetting all internal variables. 12 years ago
Sven Slootweg d3c970a551 Implement autoloading 12 years ago
Sven Slootweg a8937630e3 Set proper default for 'none' field type 12 years ago
Sven Slootweg c0a40b2bd4 Set proper defaults for html and nl2br field types 12 years ago
Sven Slootweg 110ba159d2 Implement isempty operation 12 years ago
Sven Slootweg ecb95ca151 Implement isset operation in template If statements 12 years ago
Sven Slootweg 7034837966 Throw proper exceptions if a variable in a template cannot be found. 12 years ago
Sven Slootweg 890296f25e Add fix_utf8 function. 12 years ago
Sven Slootweg bc68bf8969 We shouldn't be pretty_dump()ing the query in production code 12 years ago
Sven Slootweg a1efe9b400 Throw an exception when the class prototype does not match the database schema. 12 years ago
Sven Slootweg ca9808f9e2 Also default to null when no defaultable array is specified, for the ConstructDataset function 12 years ago
Sven Slootweg 67c2b55ab6 Fix some stray spaces 12 years ago
Sven Slootweg 7a933cd60d Fix years calculation and use properties instead of formatting function for time difference calculation. 12 years ago
Sven Slootweg bd3db37ea5 Set a property to its default value if the field is defaultable and the referenced object cannot be found in the database. 12 years ago
Sven Slootweg d27086aca7 Only check the defaultable array if it is, in fact, an array. 12 years ago
Sven Slootweg 56b741bbee If we don't define unsafe default values, database insertion will probably break. 12 years ago
Sven Slootweg 7b625f6965 Only throw a NotFoundException during recursive retrieval if the problematic field is not defaultable 12 years ago
Sven Slootweg 641a5fe43e Remove ownership checks - this belongs in application code, not in a framework. 12 years ago
Sven Slootweg 8b437c6a4b Include the field on which retrieval from database failed when throwing a NotFoundException in a recursive retrieval. 12 years ago
Sven Slootweg 42bdba7ce0 Remove old cut_text function 12 years ago
Sven Slootweg 589dbae474 Replace cut_text with own regex-based version 12 years ago
Sven Slootweg 4245c1f206 Add cut_text option 12 years ago
Sven Slootweg b87ee4671b Set default values for blank objects 12 years ago
Sven Slootweg 7c230224f9 Add time_ago function for generating a textual representation of time difference. 12 years ago
Sven Slootweg 62064095ad Oops, I broke something 12 years ago
Sven Slootweg 69c4c67927 Fixed memcache variable name to prevent conflicts 12 years ago
Sven Slootweg 56001c5b95 Add simple RSS parser 12 years ago
Sven Slootweg fe096eac5c Only send out Unicode headers if the current document is HTML, as indicated by a switch 12 years ago
Sven Slootweg f42d47d454 Unicode, damnit\! 12 years ago
Sven Slootweg d749e53352 Deprecate a few legacy functions. 12 years ago
Sven Slootweg e9c46d4182 Add HTML tag and attribute stripping.\nAdd HTML filtering options. 12 years ago
Sven Slootweg e864e912d0 Properly handle the allow_slash parameter for regexes that have one or more end-of-string characters. 12 years ago
Sven Slootweg f4f15d9cd1 Save request path in Router object 12 years ago
Sven Slootweg 35aad80cdc We don't need that there 12 years ago
Sven Slootweg 5842bf2b72 Bugfix to make uParameters properly available when routing a request 12 years ago
Sven Slootweg 44196de152 Bugfix for incorrect handling of boolean values in FetchVariable function 12 years ago
Sven Slootweg d990ffaed4 Add hex/RGB conversion functions 12 years ago
Sven Slootweg 63702fc9db Removed obsolete type 12 years ago
Sven Slootweg 98d7e64771 Give locales a name field that specifies the original defined locale name 12 years ago
Sven Slootweg 2b91a34206 Move templater constants outside the Templater class 12 years ago
Sven Slootweg d36d283b7b Use uVariables instead of sVariables for unfiltered router variables 12 years ago
Sven Slootweg 2d634b14d7 Support configuring of variables for advanced routes 12 years ago
Sven Slootweg c66efa4bae Allow more routing options like authenticators 12 years ago
Sven Slootweg 9b0058d199 Optimize Templater::Localize and add support for new format 12 years ago
Sven Slootweg 597d8d0e56 Clean up Templater code 12 years ago
Sven Slootweg f910045417 Clean up Templater code 12 years ago
Sven Slootweg 70baf0b182 Move tree visualization to debug variable 12 years ago
Sven Slootweg 0e027f73bd Finished variable substitution 12 years ago
Sven Slootweg da9e963602 First steps for variable substitution 12 years ago
Sven Slootweg 93e7510cc5 ForEach/If evaluation done, including data subsets 12 years ago
Sven Slootweg 261ea3b8b4 Add foreach logic 12 years ago
Sven Slootweg 2b575d4f0e Clean up some unneeded parser code 12 years ago
Sven Slootweg d254d77c27 Finish template parsing 12 years ago
Sven Slootweg 1cfd53deb4 Basic template parser 12 years ago
Sven Slootweg 61a435863d Added ignore for non-sample config files 12 years ago
Sven Slootweg 9254b0594b Merged back CPHP updates from CVM and Anontune 12 years ago
Sven Slootweg b40139d102 Removed debugging info 12 years ago
Sven Slootweg 7aee896ebc Added if and foreach statements to Templater 12 years ago

2
.gitignore vendored

@ -0,0 +1,2 @@
config.php
config.mysql.php

@ -13,7 +13,8 @@
require("include.constants.php");
require("cphp/config.php");
require("include.config.php");
require("include.debug.php");
require("include.dependencies.php");
require("include.exceptions.php");
@ -23,19 +24,271 @@ require("include.misc.php");
require("include.memcache.php");
require("include.mysql.php");
require("include.session.php");
require("include.csrf.php");
require("include.forms.php");
require("include.lib.php");
require("class.templater.php");
require("class.localizer.php");
$locale = new Localizer();
$locale->Load($cphp_locale_name);
require("include.locale.php");
setlocale(LC_ALL, $locale->locale);
if(empty($not_html))
{
header("Content-Type:text/html; charset=UTF-8");
}
require("class.base.php");
require("class.databaserecord.php");
foreach($cphp_components as $component)
foreach($cphp_config->components as $component)
{
require("components/component.{$component}.php");
}
/* lighttpd (and perhaps some other HTTPds) won't pass on GET parameters
* when using the server.error-handler-404 directive that is required to
* use the CPHP router. This patch will try to detect such problems, and
* manually extract the GET data from the request URI. I admit, it's a
* bit of a hack, but there doesn't really seem to be a different way of
* solving this issue. */
/* Detect whether the request URI and the $_GET array disagree on the
* existence of GET parameters. */
if(strpos($_SERVER['REQUEST_URI'], "?") !== false && empty($_GET))
{
/* Separate the protocol/host/path component from the query string. */
list($uri, $query) = explode("?", $_SERVER['REQUEST_URI'], 2);
/* Store the entire query string in the relevant $_SERVER variable -
* lighttpds strange behaviour breaks this variable as well. */
$_SERVER['QUERY_STRING'] = $query;
/* Finally, run the query string through PHPs own internal GET data
* parser, and have it store the result in the $_GET variable. This
* should yield an identical result to a well-functioning HTTPd. */
parse_str($query, $_GET);
}
if(get_magic_quotes_gpc())
{
/* By default, get rid of all quoted variables. Magic quotes are evil. */
foreach($_POST as &$var)
{
$var = stripslashes($var);
}
foreach($_GET as &$var)
{
$var = stripslashes($var);
}
}
if(!empty($cphp_config->autoloader))
{
function cphp_autoload_class($class_name)
{
global $_APP;
$class_name = str_replace("\\", "/", strtolower($class_name));
if(file_exists("classes/{$class_name}.php"))
{
require_once("classes/{$class_name}.php");
}
}
spl_autoload_register('cphp_autoload_class');
}
/* https://stackoverflow.com/a/1159235/1332715 */
set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
if(!(error_reporting() & $errno))
return;
switch($errno) {
case E_WARNING :
case E_USER_WARNING :
case E_STRICT :
case E_NOTICE :
case E_USER_NOTICE :
$type = 'warning';
$fatal = false;
break;
default :
$type = 'fatal error';
$fatal = true;
break;
}
$trace = array_reverse(debug_backtrace());
array_pop($trace);
if(php_sapi_name() == 'cli') {
echo 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
foreach($trace as $item)
echo ' ' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()' . "\n";
} else {
echo '<p class="error_backtrace">' . "\n";
echo ' Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
echo ' <ol>' . "\n";
foreach($trace as $item)
echo ' <li>' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()</li>' . "\n";
echo ' </ol>' . "\n";
echo '</p>' . "\n";
}
if(ini_get('log_errors')) {
$items = array();
foreach($trace as $item)
$items[] = (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()';
$message = 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ': ' . join(' | ', $items);
error_log($message);
}
if($fatal)
exit(1);
});
set_exception_handler(function($e){
/* Intentionally not using the templater here; any inner exceptions
* cause serious debugging issues. Avoiding potential issues by just
* hardcoding the response here, with no code that could raise an
* exception. */
$exception_class = get_class($e);
$exception_message = $e->getMessage();
$exception_file = $e->getFile();
$exception_line = $e->getLine();
$exception_trace = $e->getTraceAsString();
error_log("Uncaught {$exception_class} in {$exception_file}:{$exception_line} ({$exception_message}). Traceback: {$exception_trace}");
switch(strtolower(ini_get('display_errors')))
{
case "1":
case "on":
case "true":
$inner_exceptions = array();
$inner_e = $e;
while(true)
{
$inner_e = $inner_e->getPrevious();
if($inner_e === null)
{
break;
}
else
{
$inner_exceptions[] = array($inner_e->getMessage(), $inner_e->getTraceAsString());
}
}
if(empty($inner_exceptions))
{
$inner_traces = "";
}
else
{
$inner_traces = "<h2>One or more previous exceptions were also recorded.</h2>";
}
foreach($inner_exceptions as $inner_e)
{
$inner_traces .= "
<p>
<span class='message'>{$inner_e[0]}</span>
</p>
<pre>{$inner_e[1]}</pre>
";
}
$error_body = "
<p>
An uncaught <span class='detail'>{$exception_class}</span> was thrown, in <span class='detail'>{$exception_file}</span> on line <span class='detail'>{$exception_line}</span>.
</p>
<p>
<span class='message'>{$exception_message}</span>
</p>
<pre>{$exception_trace}</pre>
{$inner_traces}
<p><strong>Important:</strong> These errors should never be displayed on a production server! Make sure that <em>display_errors</em> is turned off in your PHP configuration, if you want to hide these tracebacks.</p>
";
break;
default:
$error_body = "
<p>
Something went wrong while creating this page, but we're not yet quite sure what it was.
</p>
<p>
If the issue persists, please contact the administrator for this application or website.
</p>
";
break;
}
http_status_code(500);
echo("
<!doctype html>
<html>
<head>
<title>An unexpected error occurred.</title>
<style>
body
{
margin: 24px auto;
padding: 24px 16px;
font-family: sans-serif;
font-size: 18px;
width: 960px;
color: #676767;
}
h1
{
border-bottom: 2px solid black;
color: #444444;
font-size: 26px;
padding-bottom: 6px;
}
h2
{
color: #575757;
border-bottom: 2px solid #444444;
padding-top: 22px;
padding-bottom: 6px;
font-size: 21px;
}
pre
{
overflow: auto;
font-size: 13px;
color: black;
padding: 10px;
border: 1px solid gray;
border-radius: 6px;
background-color: #F8F8F8;
}
.message
{
font-weight: bold;
color: #5B0000;
}
.detail
{
color: black;
}
</style>
</head>
<body>
<h1>An unexpected error occurred.</h1>
{$error_body}
</body>
</html>
");
die();
});

@ -19,6 +19,8 @@ class CPHPBaseClass
public function RenderTimeAgo($template, $property)
{
/* DEPRECATED: Please do not use this function if you can avoid it.
* The time_ago function can now be used to accomplish the same. */
global $locale;
$variable_name = "s{$property}";
@ -130,11 +132,18 @@ class CPHPBaseClass
public function RenderTemplateExternal($template, $strings)
{
/* DEPRECATED: Please do not use this function.
* Instead, you can use Templater::AdvancedParse for rendering arbitrary templates
* without instantiating a Templater yourself. */
return $this->DoRenderTemplate($template, $strings);
}
public function DoRenderTemplate($template, $strings)
{
/* DEPRECATED: Please do not use this function.
* Class-specific templater functions have been discontinued. Instead, you can use
* Templater::AdvancedParse for rendering templates without instantiating a Templater
* yourself. */
global $locale;
try

@ -19,54 +19,95 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
public $verify_query = "";
public $table_name = "";
public $query_cache = 60;
public $id_field = "Id";
public $autoloading = true;
public $prototype = array();
public $prototype_render = array();
public $prototype_export = array();
public $uData = array();
public $sIsNewObject = true;
public $sId = 0;
public function __construct($uDataSource, $uCommunityId = 0)
public function __construct($uDataSource = 0, $defaultable = null)
{
global $cphp_config;
if(!isset($cphp_config->class_map))
{
$this->ConstructDataset($uDataSource, $uCommunityId);
die("No class map was specified. Refer to the CPHP manual for instructions.");
}
$this->ConstructDataset($uDataSource);
$this->EventConstructed();
}
public function ConstructDataset($uDataSource, $uCommunityId = 0)
public function __get($name)
{
$bind_datasets = true;
/* TODO: Don't overwrite current value in uVariable when sVariable is requested and uVariable is already set. */
if(is_numeric($uDataSource))
{
if($uDataSource != 0)
if($name[0] == "s" || $name[0] == "u")
{
if(!empty($this->fill_query))
{
$this->sId = (is_numeric($uDataSource)) ? $uDataSource : 0;
$actual_name = substr($name, 1);
$found = false;
$query = sprintf($this->fill_query, $uDataSource);
if($result = mysql_query_cached($query, $this->query_cache))
foreach($this->prototype as $type => $dataset)
{
$uDataSource = $result->data[0];
if(isset($dataset[$actual_name]))
{
$found = true;
$found_type = $type;
$found_field = $dataset[$actual_name];
}
else
}
if($found === false)
{
$classname = get_class($this);
throw new NotFoundException("Could not locate {$classname} {$uDataSource} in database.");
throw new PrototypeException("The {$actual_name} variable was not found in the prototype of the {$classname} class.");
}
$this->SetField($found_type, $actual_name, $found_field);
return $this->$name;
}
else
}
public function RefreshData()
{
$classname = get_class($this);
throw new PrototypeException("No fill query defined for {$classname} class.");
$this->PurgeCache();
$this->ConstructDataset($this->sId, 0);
if($this->autoloading === true)
{
$this->PurgeVariables();
}
}
else
public function PurgeVariables()
{
$bind_datasets = false;
foreach($this->prototype as $type => $dataset)
{
foreach($dataset as $key => $field)
{
$variable_name_safe = "s" . $key;
$variable_name_unsafe = "u" . $key;
unset($this->$variable_name_safe);
unset($this->$variable_name_unsafe);
}
}
elseif(is_object($uDataSource))
}
public function ConstructDataset($uDataSource, $expiry = -1)
{
global $database;
$bind_datasets = true;
if(is_object($uDataSource))
{
if(isset($uDataSource->data[0]))
{
@ -84,6 +125,39 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
$uDataSource = $uDataSource[0];
}
}
elseif(is_string($uDataSource) || is_numeric($uDataSource))
{
if($uDataSource !== 0)
{
if(!empty($this->fill_query))
{
/* TODO: Figure out a way to store the ID internally without post-processing... */
$this->sId = htmlspecialchars($uDataSource);
$expiry = ($expiry == -1) ? $this->query_cache : $expiry;
/* Use PDO to fetch the object from the database. */
if($result = $database->CachedQuery($this->fill_query, array(":Id" => (string) $uDataSource), $expiry))
{
$uDataSource = $result->data[0];
}
else
{
$classname = get_class($this);
throw new NotFoundException("Could not locate {$classname} {$uDataSource} in database.", 0, null, "");
}
}
else
{
$classname = get_class($this);
throw new PrototypeException("No fill query defined for {$classname} class.");
}
}
else
{
$bind_datasets = false;
$this->FillDefaults();
}
}
else
{
$classname = get_class($this);
@ -92,13 +166,17 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
if($bind_datasets === true)
{
$this->sId = (is_numeric($uDataSource['Id'])) ? $uDataSource['Id'] : 0;
$this->sId = htmlspecialchars($uDataSource[$this->id_field]);
$this->sIsNewObject = false;
$this->uData = $uDataSource;
if($this->autoloading === false)
{
foreach($this->prototype as $type => $dataset)
{
$this->BindDataset($type, $dataset);
$this->BindDataset($type, $dataset, $defaultable);
}
}
$this->sFound = true;
@ -107,29 +185,47 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
{
$this->sFound = false;
}
}
if(!empty($uCommunityId) && !empty($this->sCommunityId))
public function BindDataset($type, $dataset, $defaultable)
{
$sCommunityId = (is_numeric($uCommunityId)) ? $uCommunityId : 0;
global $cphp_config;
if($sCommunityId != $this->sCommunity->sId)
if(is_array($dataset))
{
$classname = get_class($this);
throw new OwnershipException("{$classname} {$this->sId} does not belong to Community {$sCommunityId}.");
foreach($dataset as $variable_name => $column_name)
{
$this->SetField($type, $variable_name, $column_name);
}
}
else
{
$classname = get_class($this);
throw new Exception("Invalid dataset passed on to {$classname}.BindDataset.");
}
}
public function BindDataset($type, $dataset)
public function SetField($type, $variable_name, $column_name)
{
global $cphp_class_map;
global $cphp_config;
if(is_array($dataset))
{
foreach($dataset as $variable_name => $column_name)
if(!isset($this->uData[$column_name]))
{
throw new Exception("The column name {$column_name} was not found in the resultset - ensure the prototype corresponds to the table schema.");
}
$original_value = $this->uData[$column_name];
if($original_value === "" && ($type == "timestamp" || $type == "numeric" || $type == "boolean"))
{
$variable_name_safe = "s" . $variable_name;
$this->$variable_name_safe = null;
$variable_name_unsafe = "u" . $variable_name;
$this->$variable_name_unsafe = null;
}
else
{
switch($type)
{
case "string":
@ -160,21 +256,25 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
$value = (empty($original_value)) ? false : true;
$variable_type = CPHP_VARIABLE_SAFE;
break;
case "user":
$value = new User($original_value);
$variable_type = CPHP_VARIABLE_SAFE;
break;
case "none":
$value = $original_value;
$variable_type = CPHP_VARIABLE_UNSAFE;
break;
default:
$found = false;
foreach($cphp_class_map as $class_type => $class_name)
foreach(get_object_vars($cphp_config->class_map) as $class_type => $class_name)
{
if($type == $class_type)
{
try
{
$value = new $class_name($original_value);
}
catch (NotFoundException $e)
{
$e->field = $variable_name;
throw $e;
}
$variable_type = CPHP_VARIABLE_SAFE;
$found = true;
}
@ -198,15 +298,54 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
$this->$variable_name_unsafe = $original_value;
}
}
else
public function FillDefaults()
{
$classname = get_class($this);
throw new Exception("Invalid dataset passed on to {$classname}.BindDataset.");
foreach($this->prototype as $type => $dataset)
{
switch($type)
{
case "string":
case "simplehtml":
case "html":
case "nl2br":
case "none":
$safe_default_value = "";
$unsafe_default_value = "";
break;
case "numeric":
$safe_default_value = 0;
$unsafe_default_value = "0";
break;
case "boolean":
$safe_default_value = false;
$unsafe_default_value = "0";
break;
case "timestamp":
$safe_default_value = null;
$unsafe_default_value = null;
break;
default:
continue 2;
}
foreach($dataset as $property)
{
$safe_variable_name = "s" . $property;
$this->$safe_variable_name = $safe_default_value;
$unsafe_variable_name = "u" . $property;
$this->$unsafe_variable_name = $unsafe_default_value;
}
}
}
public function DoRenderInternalTemplate()
{
/* DEPRECATED: Please do not use this function.
* Class-specific templater functions have been discontinued. Instead, you can use
* Templater::AdvancedParse for rendering templates without instantiating a Templater
* yourself. */
if(!empty($this->render_template))
{
$strings = array();
@ -224,18 +363,25 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
}
}
public function InsertIntoDatabase()
public function InsertIntoDatabase($force_data = false)
{
global $cphp_config, $database;
if(!empty($this->verify_query))
{
if($this->sId == 0)
if(strpos($this->verify_query, ":Id") === false)
{
throw new DeprecatedException("Support for mysql_* has been removed from CPHP. Please update your queries to be in CachedPDO-style.");
}
if($this->sIsNewObject === true)
{
$insert_mode = CPHP_INSERTMODE_INSERT;
}
else
{
$query = sprintf($this->verify_query, $this->sId);
if($result = mysql_query_cached($query))
/* FIXME: This can probably be optimized... */
if($result = $database->CachedQuery($this->verify_query, array(":Id" => $this->sId), 0))
{
$insert_mode = CPHP_INSERTMODE_UPDATE;
}
@ -245,6 +391,29 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
}
}
if($force_data === true)
{
foreach($this->prototype as $type_key => $type_value)
{
foreach($type_value as $element_key => $element_value)
{
$variable_name_unsafe = "u" . $element_key;
if(!isset($this->$variable_name_unsafe))
{
foreach($this->prototype as $type => $dataset)
{
if(isset($dataset[$element_key]))
{
$column_name = $dataset[$element_key];
$this->$variable_name_unsafe = $this->uData[$column_name];
}
}
}
}
}
}
$element_list = array();
foreach($this->prototype as $type_key => $type_value)
@ -258,6 +427,9 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
case "boolean":
case "timestamp":
case "string":
case "simplehtml":
case "html":
case "nl2br":
$element_list[$element_value] = array(
'key' => $element_key,
'type' => $type_key
@ -270,7 +442,8 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
}
$sKeyList = array();
$sValueList = array();
$sKeyIdentifierList = array();
$uValueList = array();
foreach($element_list as $sKey => $value)
{
@ -282,77 +455,121 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
switch($value['type'])
{
case "none":
$sFinalValue = mysql_real_escape_string($this->$variable_name_unsafe);
$uFinalValue = $this->$variable_name_unsafe;
break;
case "numeric":
$number = (isset($this->$variable_name_unsafe)) ? $this->$variable_name_unsafe : $this->$variable_name_safe;
$sFinalValue = (is_numeric($number)) ? $number : 0;
$uFinalValue = (is_numeric($number)) ? $number : 0;
break;
case "boolean":
$bool = (isset($this->$variable_name_unsafe)) ? $this->$variable_name_unsafe : $this->$variable_name_safe;
$sFinalValue = ($bool) ? "1" : "0";
$uFinalValue = ($bool) ? "1" : "0";
break;
case "timestamp":
$sFinalValue = (isset($this->$variable_name_safe)) ? mysql_from_unix($this->$variable_name_safe) : mysql_from_unix(unix_from_local($this->$variable_name_unsafe));
if(is_numeric($this->$variable_name_unsafe))
{
$uFinalValue = mysql_from_unix($this->$variable_name_unsafe);
}
else
{
if(isset($this->$variable_name_safe))
{
$uFinalValue = mysql_from_unix($this->$variable_name_safe);
}
else
{
$uFinalValue = mysql_from_unix(unix_from_local($this->$variable_name_unsafe));
}
}
break;
case "string":
$sFinalValue = (isset($this->$variable_name_unsafe)) ? mysql_real_escape_string($this->$variable_name_unsafe) : mysql_real_escape_string($this->$variable_name_safe);
case "simplehtml":
case "html":
case "nl2br":
$uFinalValue = (isset($this->$variable_name_unsafe)) ? $this->$variable_name_unsafe : $this->$variable_name_safe;
break;
case "default":
$sFinalValue = mysql_real_escape_string($this->$variable_name_unsafe);
$uFinalValue = $this->$variable_name_unsafe;
break;
}
$sFinalValue = "'{$sFinalValue}'";
$sKey = "`{$sKey}`";
$sIdentifier = ":{$sKey}";
$sKeyList[] = $sKey;
$sValueList[] = $sFinalValue;
$sKeyList[] = "`{$sKey}`";
$sKeyIdentifierList[] = $sIdentifier;
$uValueList[$sIdentifier] = $uFinalValue;
}
else
{
if($this->autoloading === false)
{
$classname = get_class($this);
throw new Exception("Database insertion failed: prototype property {$value['key']} not found in object of type {$classname}.");
}
}
}
if($insert_mode == CPHP_INSERTMODE_INSERT)
{
if(!empty($this->uId))
{
$sIdentifier = ":{$this->id_field}";
$sKeyList[] = "`{$this->id_field}`";
$sKeyIdentifierList[] = $sIdentifier;
$uValueList[$sIdentifier] = $this->uId;
}
$sQueryKeys = implode(", ", $sKeyList);
$sQueryValues = implode(", ", $sValueList);
$query = "INSERT INTO {$this->table_name} ({$sQueryKeys}) VALUES ({$sQueryValues})";
$sQueryKeyIdentifiers = implode(", ", $sKeyIdentifierList);
$query = "INSERT INTO {$this->table_name} ({$sQueryKeys}) VALUES ({$sQueryKeyIdentifiers})";
}
elseif($insert_mode == CPHP_INSERTMODE_UPDATE)
{
$sKeyValueList = array();
$sKeysIdentifiersList = array();
for($i = 0; $i < count($sKeyList); $i++)
{
$sKey = $sKeyList[$i];
$sValue = $sValueList[$i];
$sKeyValueList[] = "{$sKey} = {$sValue}";
$sValue = $sKeyIdentifierList[$i];
$sKeysIdentifiersList[] = "{$sKey} = {$sValue}";
}
$sQueryKeysValues = implode(", ", $sKeyValueList);
$query = "UPDATE {$this->table_name} SET {$sQueryKeysValues} WHERE `Id` = '{$this->sId}'";
$sQueryKeysIdentifiers = implode(", ", $sKeysIdentifiersList);
/* We use :CPHPID here because it's unlikely to be used in the application itself. */
$query = "UPDATE {$this->table_name} SET {$sQueryKeysIdentifiers} WHERE `{$this->id_field}` = :CPHPID";
$uValueList[':CPHPID'] = $this->sId;
}
if($result = mysql_query($query))
try
{
$result = $database->CachedQuery($query, $uValueList, 0);
if($insert_mode == CPHP_INSERTMODE_INSERT)
{
$this->sId = mysql_insert_id();
$this->sId = $database->lastInsertId();
$this->sIsNewObject = false;
}
$this->PurgeCache();
$this->RefreshData();
return $result;
}
else
catch (DatabaseException $e)
{
$classname = get_class($this);
throw new DatabaseException("Database insertion query failed in object of type {$classname}. Error message: " . mysql_error());
$error = $database->errorInfo();
if(empty($error[2]))
{
$errmsg = $e->getMessage();
}
else
{
$errmsg = $error[2];
}
throw new DatabaseException("Database insertion query failed in object of type {$classname}. Error message: " . $errmsg);
}
}
else
@ -364,13 +581,15 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
public function RetrieveChildren($type, $field)
{
if(!isset($cphp_class_map[$type]))
/* Probably won't ever be fully implemented, now that there is CreateFromQuery. */
if(!isset($cphp_config->class_map->$type))
{
$classname = get_class($this);
throw new NotFoundException("Non-existent 'type' argument passed on to {$classname}.RetrieveChildren function.");
}
$parent_type = get_parent_class($cphp_class_map[$type]);
$parent_type = get_parent_class($cphp_config->class_map->$type);
if($parent_type !== "CPHPDatabaseRecordClass")
{
$parent_type = ($parent_type === false) ? "NONE" : $parent_type;
@ -383,9 +602,13 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
public function PurgeCache()
{
$query = sprintf($this->fill_query, $this->sId);
$key = md5($query) . md5($query . "x");
mc_delete($key);
$parameters = array(":Id" => (string) $this->sId);
$query_hash = md5($this->fill_query);
$parameter_hash = md5(serialize($parameters));
$cache_hash = $query_hash . $parameter_hash;
mc_delete($cache_hash);
}
public function RenderTemplate($template = "")
@ -398,6 +621,69 @@ abstract class CPHPDatabaseRecordClass extends CPHPBaseClass
return $this->DoRenderInternalTemplate();
}
public function Export()
{
/* This function is DEPRECATED and should not be used. Please manually build your arrays instead. */
$export_array = array();
foreach($this->prototype_export as $field)
{
$variable_name = "s{$field}";
if(is_object($this->$variable_name))
{
if(!empty($this->$variable_name->sId))
{
$export_array[$field] = $this->$variable_name->Export();
}
else
{
$export_array[$field] = null;
}
}
else
{
$export_array[$field] = $this->$variable_name;
}
}
return $export_array;
}
public static function CreateFromQuery($query, $parameters = array(), $expiry = 0, $first_only = false)
{
global $database;
$result = $database->CachedQuery($query, $parameters, $expiry);
if($result)
{
if($first_only === true)
{
/* TODO: Try to run the query with LIMIT 1 if only the first result is desired. */
return new static($result);
}
elseif(count($result->data) == 1)
{
return array(new static($result));
}
else
{
$result_array = array();
foreach($result->data as $row)
{
$result_array[] = new static($row);
}
return $result_array;
}
}
else
{
throw new NotFoundException("No results for specified query.");
}
}
// Define events
protected function EventConstructed() { }

@ -22,6 +22,7 @@ class Localizer
public $date_short = "";
public $date_long = "";
public $time = "";
public $name = "";
public function Load($locale)
{
@ -32,8 +33,14 @@ class Localizer
public function LoadInternal($locale)
{
global $cphp_locale_path, $cphp_locale_ext;
$lng_contents = file_get_contents("{$cphp_locale_path}/{$locale}.{$cphp_locale_ext}");
global $cphp_config;
if(!isset($cphp_config->locale->path) || !isset($cphp_config->locale->extension))
{
throw new Exception("The locale path settings are not specified correctly. Refer to the CPHP manual for instructions.");
}
$lng_contents = file_get_contents("{$cphp_config->locale->path}/{$locale}.{$cphp_config->locale->extension}");
if($lng_contents !== false)
{
$lines = explode("\n", $lng_contents);
@ -70,6 +77,8 @@ class Localizer
}
}
}
$this->name = $locale;
}
else
{

File diff suppressed because it is too large Load Diff

@ -42,31 +42,27 @@ class CPHPErrorHandler
{
global $locale;
$template['error'] = new Templater();
switch($this->sErrorType)
{
case CPHP_ERRORHANDLER_TYPE_ERROR:
$template['error']->Load("errorhandler.error");
$template = "errorhandler.error";
break;
case CPHP_ERRORHANDLER_TYPE_INFO:
$template['error']->Load("errorhandler.info");
$template = "errorhandler.info";
break;
case CPHP_ERRORHANDLER_TYPE_WARNING:
$template['error']->Load("errorhandler.warning");
$template = "errorhandler.warning";
break;
case CPHP_ERRORHANDLER_TYPE_SUCCESS:
$template['error']->Load("errorhandler.success");
$template = "errorhandler.success";
break;
default:
return false;
}
$template['error']->Localize($locale->strings);
$template['error']->Compile(array(
return Templater::AdvancedParse($template, $locale->strings, array(
'title' => $this->sTitle,
'message' => $this->sMessage
));
return $template['error']->Render();
}
}
?>

@ -1,319 +0,0 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
cphp_dependency_provides("cphp_formbuilder", "1.0");
$cphp_formbuilder_increment = 0;
abstract class CPHPFormBuilderBaseClass
{
public $parameters = array();
public function AddParameter($key, $value)
{
$this->parameters[$key] = $value;
}
public function RenderParameters($parameters)
{
if(empty($parameters))
{
return "";
}
$rendered = array();
foreach($parameters as $key => $value)
{
$value = utf8entities($value);
$rendered[] = "{$key}=\"{$value}\"";
}
return " " . implode(" ", $rendered);
}
public function RenderNote()
{
if(!empty($this->note))
{
return "<div class=\"cphp_fbd_note\">{$this->note}</div>";
}
else
{
return "";
}
}
}
abstract class CPHPFormBuilderContainer extends CPHPFormBuilderBaseClass
{
public $elements = array();
public function AddElement($element)
{
$this->elements[] = $element;
}
}
class CPHPFormBuilder extends CPHPFormBuilderContainer
{
public $method = "";
public $action = "";
public function __construct($method, $target)
{
$this->method = strtolower($method);
$this->action = $target;
}
public function Render()
{
$rendered_elements = "";
foreach($this->elements as $element)
{
$rendered_elements .= $element->Render();
}
$this->AddParameter("method", $this->method);
$this->AddParameter("action", $this->action);
$rendered_parameters = $this->RenderParameters($this->parameters);
return "<form{$rendered_parameters}>{$rendered_elements}</form>";
}
}
class CPHPFormSection extends CPHPFormBuilderContainer
{
public $label = "";
public $fieldset = true;
public $classname = "";
public function __construct($fieldset = true, $label = "")
{
if(!empty($label))
{
$this->label = $label;
}
$this->fieldset = $fieldset;
}
public function Render()
{
if(!empty($this->label))
{
$legend = "<legend>{$this->label}</legend>";
}
else
{
$legend = "";
}
if($this->fieldset === true)
{
$this->classname = trim("{$this->classname} cphp_fbd_fieldset");
}
$rendered_elements = "";
foreach($this->elements as $element)
{
$rendered_elements .= $element->Render();
}
if($this->fieldset === true)
{
$this->AddParameter("class", $this->classname);
$rendered_parameters = $this->RenderParameters($this->parameters);
return "<fieldset{$rendered_parameters}>{$legend}<div class=\"cphp_fbd_form\">{$rendered_elements}</div></fieldset>";
}
else
{
return "<div class=\"cphp_fbd_form\"{$rendered_parameters}>{$rendered_elements}</div>";
}
}
}
abstract class CPHPFormInputElement extends CPHPFormBuilderBaseClass
{
public $id = "";
public $name = "";
public $value = "";
public $label = "";
public function __construct($label, $name, $value = "", $note = "", $id = "")
{
global $cphp_formbuilder_increment;
$this->name = $name;
$this->value = $value;
$this->label = $label;
$this->note = $note;
if(empty($id))
{
$this->id = "cphp_fbd_{$cphp_formbuilder_increment}";
$cphp_formbuilder_increment += 1;
}
else
{
$this->id = $id;
}
}
}
abstract class CPHPFormInput extends CPHPFormInputElement
{
public function Render()
{
$this->AddParameter("id", $this->id);
$this->AddParameter("type", $this->type);
$this->AddParameter("name", $this->name);
$this->AddParameter("value", $this->value);
$rendered_parameters = $this->RenderParameters($this->parameters);
$rendered_note = $this->RenderNote();
return "<div class=\"cphp_fbd_row\"><div class=\"cphp_fbd_label\">{$this->label}{$rendered_note}</div><div class=\"cphp_fbd_field\"><input{$rendered_parameters}></div></div>";
}
}
class CPHPFormTextInput extends CPHPFormInput
{
public $type = "text";
}
class CPHPFormPasswordInput extends CPHPFormInput
{
public $type = "password";
}
class CPHPFormDateInput extends CPHPFormInput
{
public $type = "date";
}
class CPHPFormTimeInput extends CPHPFormInput
{
public $type = "time";
}
class CPHPFormEmailInput extends CPHPFormInput
{
public $type = "email";
}
class CPHPFormUrlInput extends CPHPFormInput
{
public $type = "url";
}
class CPHPFormRangeInput extends CPHPFormInput
{
public $type = "range";
}
class CPHPFormColorInput extends CPHPFormInput
{
public $type = "color";
}
class CPHPFormSearchInput extends CPHPFormInput
{
public $type = "search";
}
class CPHPFormCheckboxGroup extends CPHPFormBuilderContainer
{
public function __construct($label, $note = "")
{
global $cphp_formbuilder_increment;
$this->label = $label;
$this->note = $note;
}
public function Render()
{
$rendered_note = $this->RenderNote();
$rendered_elements = "";
foreach($this->elements as $element)
{
$rendered_elements .= $element->Render();
}
return "<div class=\"cphp_fbd_row\"><div class=\"cphp_fbd_label\">{$this->label}{$rendered_note}</div><div class=\"cphp_fbd_field\">{$rendered_elements}</div></div>";
}
}
class CPHPFormCheckbox extends CPHPFormInputElement
{
public function Render()
{
$this->AddParameter("id", $this->id);
$this->AddParameter("type", "checkbox");
$this->AddParameter("name", $this->name);
if($this->value === true)
{
$this->AddParameter("checked", "");
}
$rendered_parameters = $this->RenderParameters($this->parameters);
$rendered_note = $this->RenderNote();
return "<div class=\"cphp_fbd_cblabel\"><input{$rendered_parameters}><label for=\"{$this->id}\">{$this->label}{$rendered_note}</label></div>";
}
}
class CPHPFormRadioButton extends CPHPFormInput
{
public $type = "radio";
}
class CPHPFormButton extends CPHPFormInputElement
{
public $type = "button";
public function Render()
{
$this->AddParameter("type", $this->type);
$this->AddParameter("name", $this->name);
$this->AddParameter("value", $this->value);
$rendered_parameters = $this->RenderParameters($this->parameters);
return "<div class=\"cphp_fbd_row\"><div class=\"cphp_fbd_label\"></div><div class=\"cphp_fbd_field\"><button{$rendered_parameters}>{$this->label}</button></div></div>";
}
}
class CPHPFormSubmitButton extends CPHPFormButton
{
public $type = "submit";
}
class CPHPFormResetButton extends CPHPFormButton
{
public $type = "reset";
}
class CPHPFormSelect extends CPHPFormInputElement
{
public $options = array();
}
?>

@ -11,13 +11,18 @@
* licensing text.
*/
cphp_dependency_provides("cphp_router", "1.0");
cphp_dependency_provides("cphp_router", "1.1");
class RouterException extends Exception {}
class CPHPRouter extends CPHPBaseClass
{
public $routes = array();
public $parameters = array();
public $uVariables = array();
public $custom_query = "";
public $allow_slash = false;
public $ignore_query = false;
public function RouteRequest()
{
@ -39,6 +44,17 @@ class CPHPRouter extends CPHPBaseClass
}
}
// Save request path in Router object to make it accessible to other scripts.
$this->uRequestPath = $requestpath;
if($this->ignore_query === true)
{
if(strpos($requestpath, "?") !== false)
{
list($requestpath, $bogus) = explode("?", $requestpath, 2);
}
}
$found = false; // Workaround because a break after an include apparently doesn't work in PHP.
foreach($this->routes as $priority)
@ -47,16 +63,101 @@ class CPHPRouter extends CPHPBaseClass
{
if($found === false)
{
if($this->allow_slash === true)
{
if(strpos($route_regex, "$") !== false)
{
$route_regex = str_replace("$", "/?$", $route_regex);
}
else
{
$route_regex = "{$route_regex}/?";
}
}
$regex = str_replace("/", "\/", $route_regex);
if(preg_match("/{$regex}/i", $requestpath, $matches))
{
$this->uParameters = $matches;
include($route_destination);
$this->uMethod = strtolower($_SERVER['REQUEST_METHOD']);
if(is_array($route_destination))
{
// Options were provided.
if(!isset($route_destination['target']))
{
throw new InvalidArgumentException("Target is missing from CPHPRoute options element.");
}
if(!empty($route_destination['methods']))
{
$sMethods = (!is_array($route_destination['methods'])) ? array($route_destination['methods']) : $route_destination['methods'];
if(!in_array($this->uMethod, $sMethods))
{
continue;
}
}
$authenticated = false;
if(!isset($route_destination['authenticator']))
{
$authenticated = true;
}
else
{
if(!isset($route_destination['auth_error']))
{
throw new InvalidArgumentException("When specifying an authenticator, you must also specify a default error destination.");
}
$sRouterAuthenticated = false;
$sRouterErrorDestination = $route_destination['auth_error'];
require($route_destination['authenticator']);
if($sRouterAuthenticated === true)
{
$authenticated = true;
}
}
foreach($route_destination as $key => $value)
{
if(strlen($key) > 1 && substr($key, 0, 1) == "_")
{
$key = substr($key, 1);
$this->uVariables[$key] = $value;
}
}
if($authenticated === true)
{
$destination = $route_destination['target'];
$this->sAuthenticated = true;
}
else
{
$destination = $sRouterErrorDestination;
$this->sAuthenticated = false;
}
}
else
{
$destination = $route_destination;
}
include($destination);
$found = true;
}
}
}
}
if($found === false)
{
throw new RouterException("No suitable route found");
}
}
}
?>

@ -1,42 +0,0 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if($_CPHP !== true) { die(); }
$cphp_class_map = array(
'classa' => "ClassA",
'classb' => "ClassB"
);
$cphp_locale_name = "english";
$cphp_locale_path = "locales";
$cphp_locale_ext = "lng";
$cphp_usersettings[CPHP_SETTING_TIMEZONE] = "Europe/Amsterdam";
/* These are the memcache settings. You will need to have memcache set
* up on your server to use these. Compression requires zlib. */
$cphp_memcache_enabled = true; // Whether to user memcache.
$cphp_memcache_server = "localhost"; // The hostname of the memcached
$cphp_memcache_port = 11211; // The port number of memcached
$cphp_memcache_compressed = true; // Whether to compress memcache objects
$cphp_mysql_enabled = true;
require("config.mysql.php");
$cphp_components = array(
"router",
"errorhandler",
"formbuilder"
);

@ -0,0 +1,33 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if($_CPHP !== true) { die(); }
if(empty($_CPHP_CONFIG))
{
die("No valid CPHP configuration path was specified. Refer to the CPHP manual for instructions.");
}
$confdata = @file_get_contents($_CPHP_CONFIG);
if($confdata === false)
{
die("The specified CPHP configuration path was not found. Refer to the CPHP manual for instructions.");
}
$cphp_config = @json_decode($confdata);
if(json_last_error() != JSON_ERROR_NONE)
{
die("Failed to parse CPHP configuration. Refer to the CPHP manual for instructions.");
}

@ -0,0 +1,74 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if($_CPHP !== true) { die(); }
class CSRF
{
public static function GenerateToken()
{
$key = random_string(12);
$token = random_string(25);
if(!isset($_SESSION['_CPHP_CSRF_KEYS']))
{
$_SESSION['_CPHP_CSRF_KEYS'] = array();
}
$_SESSION['_CPHP_CSRF_KEYS'][$key] = $token;
return array(
'key' => $key,
'token' => $token
);
}
public static function GenerateReplacement($matches)
{
$pair = CSRF::GenerateToken();
return $matches[0] . "
<input name=\"_CPHP_CSRF_KEY\" type=\"hidden\" value=\"{$pair['key']}\">
<input name=\"_CPHP_CSRF_TOKEN\" type=\"hidden\" value=\"{$pair['token']}\">";
}
public static function InsertTokens($input)
{
return preg_replace_callback("/<form[^>]*>(?!\s*<input name=\"_CPHP_CSRF)/i", "CSRF::GenerateReplacement", $input);
}
public static function VerifyToken($source = null)
{
if($source == null)
{
$source = $_POST;
}
if(!empty($source['_CPHP_CSRF_KEY']) && !empty($source['_CPHP_CSRF_TOKEN']))
{
$key = $source['_CPHP_CSRF_KEY'];
$token = $source['_CPHP_CSRF_TOKEN'];
if(empty($_SESSION['_CPHP_CSRF_KEYS'][$key]) || $_SESSION['_CPHP_CSRF_KEYS'][$key] != $token)
{
throw new CsrfException("The given CSRF token does not match the given CSRF key.");
}
}
else
{
throw new CsrfException("No CSRF token present in submitted data.");
}
}
}
class CsrfException extends Exception {}

@ -13,6 +13,15 @@
if($_CPHP !== true) { die(); }
if(!empty($cphp_config->locale->default_timezone))
{
$user_preferences[CPHP_SETTING_TIMEZONE] = $cphp_config->locale->default_timezone;
}
else
{
die("No default timezone was specified. Refer to the CPHP manual for instructions.");
}
$timezones = array(
'Pacific/Kwajalein' => '(GMT-12:00) International Date Line West',
'Pacific/Midway' => '(GMT-11:00) Midway Island',
@ -178,3 +187,101 @@ function floor_to_day($timestamp)
{
return floor($timestamp / (60 * 60 * 24)) * (60 * 60 * 24);
}
function time_ago($timestamp, $locale)
{
if(is_numeric($timestamp))
{
if($timestamp > time())
{
$sTimeAgo = $locale->strings['event-future'];
}
elseif($timestamp == time())
{
$sTimeAgo = $locale->strings['event-now'];
}
else
{
$date1 = new DateTime("@{$timestamp}", new DateTimeZone("GMT"));
$date2 = new DateTime("now", new DateTimeZone("GMT"));
$interval = $date1->diff($date2);
$years = (int)$interval->y;
$months = (int)$interval->m;
$weeks = (int)$interval->format("%U");
$days = (int)$interval->d;
$hours = (int)$interval->h;
$minutes = (int)$interval->i;
$seconds = (int)$interval->s;
if($years > 1)
{
$sTimeAgo = sprintf($locale->strings['event-years-ago'], $years);
}
elseif($years == 1)
{
$sTimeAgo = $locale->strings['event-1year-ago'];
}
elseif($months > 1)
{
$sTimeAgo = sprintf($locale->strings['event-months-ago'], $months);
}
elseif($months == 1)
{
$sTimeAgo = $locale->strings['event-1month-ago'];
}
elseif($weeks > 1)
{
$sTimeAgo = sprintf($locale->strings['event-weeks-ago'], $weeks);
}
elseif($weeks == 1)
{
$sTimeAgo = $locale->strings['event-1week-ago'];
}
elseif($days > 1)
{
$sTimeAgo = sprintf($locale->strings['event-days-ago'], $days);
}
elseif($days == 1)
{
$sTimeAgo = $locale->strings['event-1day-ago'];
}
elseif($hours > 1)
{
$sTimeAgo = sprintf($locale->strings['event-hours-ago'], $hours);
}
elseif($hours == 1)
{
$sTimeAgo = $locale->strings['event-1hour-ago'];
}
elseif($minutes > 1)
{
$sTimeAgo = sprintf($locale->strings['event-minutes-ago'], $minutes);
}
elseif($minutes == 1)
{
$sTimeAgo = $locale->strings['event-1minute-ago'];
}
elseif($seconds > 1)
{
$sTimeAgo = sprintf($locale->strings['event-seconds-ago'], $seconds);
}
elseif($seconds == 1)
{
$sTimeAgo = $locale->strings['event-1second-ago'];
}
else
{
/* If you see this, there's probably something wrong. */
$sTimeAgo = $locale->strings['event-past'];
}
}
return $sTimeAgo;
}
else
{
throw new Exception("The given timestamp is not numeric. Timestamps must be provided in Unix timestamp format.");
}
}

@ -0,0 +1,388 @@
<?php
$cphp_debug_start = microtime(true);
$cphp_debug_log = array();
$cphp_debug_enabled = false;
function cphp_debug_enable()
{
global $cphp_debug_enabled;
$cphp_debug_enabled = true;
}
function cphp_debug_disable()
{
global $cphp_debug_enabled;
$cphp_debug_enabled = false;
}
function cphp_debug_snapshot($data)
{
global $cphp_debug_start, $cphp_debug_log, $cphp_debug_enabled;
if($cphp_debug_enabled === true)
{
$timestamp = microtime(true) - $cphp_debug_start;
$cphp_debug_log[] = array(
'timestamp' => $timestamp,
'data' => $data
);
}
}
function cphp_debug_dump()
{
global $cphp_debug_log;
return json_encode($cphp_debug_log);
}
function cphp_debug_display($data)
{
/* We can't use the templater for this, because that would make this function unusable if the
* templater itself were to ever be the subject of the debugging. */
?>
<!doctype html>
<html>
<head>
<title>CPHP Debuglog Viewer</title>
<style>
body
{
font-family: monospace;
}
#slider
{
background-color: #DDDDDD;
position: absolute;
left: 0px;
right: 0px;
top: 0px;
height: 100px;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#slider_bar
{
background-color: #C8C8C8;
height: 24px;
position: relative;
}
#slider_handle
{
width: 24px;
height: 24px;
position: absolute;
left: 0px;
top: 0px;
background-color: #6F6F6F;
cursor: pointer;
}
#slider_handle.dragging
{
background-color: #000000;
}
#datapoint_info
{
padding: 7px;
}
#details
{
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
top: 100px;
overflow: auto;
}
.variable
{
padding-left: 48px;
border-top: 1px solid silver;
}
.variable .name
{
font-size: 15px;
font-weight: bold;
margin-right: 6px;
}
.variable .data
{
font-size: 13px;
}
a.expander
{
text-decoration: none;
color: blue;
font-weight: bold;
font-size: 14px;
}
.data.undefined
{
color: silver;
}
.data.text
{
color: #A000B2;
}
.data.numeric
{
color: red;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
var data_points = <?php echo($data); ?>;
var total_data_points = data_points.length;
var current_point = 0;
function sliderUpdate()
{
total_width = $('#slider_bar').width() - $('#slider_handle').width();
current_position = $('#slider_handle').offset().left;
point_width = total_width / (total_data_points - 1);
closest_data_point = Math.round(current_position / point_width);
current_point = closest_data_point;
real_position = closest_data_point * point_width;
$('#slider_handle').css({'left': real_position});
relevant_data_point = data_points[closest_data_point];
$('#datapoint_info').html("<strong>Data point:</strong> " + closest_data_point + "<br><strong>Timestamp:</strong> " + relevant_data_point['timestamp'])
updateTree(relevant_data_point['data']);
}
function switchDataPoint(i)
{
total_width = $('#slider_bar').width() - $('#slider_handle').width();
point_width = total_width / (total_data_points - 2);
real_position = i * point_width;
$('#slider_handle').css({'left': real_position});
current_point = i;
$('#datapoint_info').html("<strong>Data point:</strong> " + i + "<br><strong>Timestamp:</strong> " + data_points[i]['timestamp'])
updateTree(data_points[i]['data']);
}
function updateTree(data)
{
$('.variable.array').children('.variable').remove();
$('.data.text, .data.numeric').html("undefined").addClass("undefined").removeClass("text").removeClass("numeric");
updateElements(data, "root", "item");
$('a.expander').each(function(){
if($(this).text() == "[+]")
{
$(this).parent().children('.variable').hide();
}
});
hookExpanders();
}
function initializeElements()
{
/* Build a prototype for display, out of all the available datapoints. */
prototype = {};
for(x in data_points)
{
prototype = $.extend(true, prototype, data_points[x]);
}
createElements(prototype['data'], "root", "item").appendTo('#details');
}
function createElements(source, key, hierarchy)
{
var item;
var id = hierarchy + "_" + key.replace(/[^a-z0-9_]/gi,'');
if($.isArray(source))
{
/* Array. */
var me = $('<div class="variable array" id="' + id + '"><a class="expander" href="javascript:void(0);">[-]</a><span class="name">' + key + '</span><span class="data undefined">Array</span></div>');
}
else if($.isPlainObject(source))
{
/* Object. */
var me = $('<div class="variable object" id="' + id + '"><a class="expander" href="javascript:void(0);">[-]</a><span class="name">' + key + '</span><span class="data undefined">Object</span></div>');
for(item in source)
{
me.append(createElements(source[item], item, id));
}
}
else
{
/* Value. */
var me = $('<div class="variable value" id="' + id + '"><span class="name">' + key + '</span><span class="data undefined">undefined</span></div>');
}
return me;
}
function updateElements(source, key, hierarchy)
{
var item;
var id = hierarchy + "_" + key.replace(/[^a-z0-9_]/gi,'');
if($.isArray(source))
{
/* Array. */
//$('#' + id).children('.variable').remove();
for(item in source)
{
$('#' + id).append(createElements(source[item], item, id));
updateElements(source[item], item, id);
}
}
else if($.isPlainObject(source))
{
/* Object. */
for(item in source)
{
updateElements(source[item], item, id);
}
}
else
{
/* Value. */
var target = $('#' + id).children('.data');
target.html(source);
target.removeClass("undefined");
if(typeof(source) == "number")
{
target.addClass("numeric");
}
else
{
target.addClass("text");
}
}
}
function hookExpanders()
{
$('a.expander').click(function(){
if($(this).text() == "[-]")
{
/* Collapse */
$(this).text("[+]");
$(this).parent().children(".variable").hide();
}
else
{
/* Expand */
$(this).text("[-]");
$(this).parent().children(".variable").show();
}
});
}
$(function(){
var drag_start_x = 0;
var drag_start_y = 0;
var dragging_slider = false;
$('#slider_handle').mousedown(function(e){
dragging_slider = true;
parent_offset = $(this).offset();
drag_start_x = e.pageX - parent_offset.left;
$('#slider_handle').addClass("dragging");
});
$('body').mouseup(function(e){
if(dragging_slider == true)
{
dragging_slider = false;
$('#slider_handle').removeClass("dragging");
}
});
$('body').mousemove(function(e){
//$('#details').html("Dragging: "+dragging_slider+", drag_start_x: "+drag_start_x+", pageX: "+e.pageX);
if(dragging_slider == true)
{
newpos = e.pageX - drag_start_x;
if(newpos > 0 && newpos < (total_width))
{
$('#slider_handle').css({'left': newpos});
}
else if(newpos < 0)
{
$('#slider_handle').css({'left': 0});
}
else if(newpos > (total_width))
{
$('#slider_handle').css({'left': total_width});
}
sliderUpdate();
}
});
$(document).keydown(function(e){
if(e.keyCode == 37 && current_point > 0)
{
switchDataPoint(current_point - 1);
}
else if(e.keyCode == 39 && current_point < total_data_points - 2)
{
switchDataPoint(current_point + 1);
}
});
var total_width = $('#slider_bar').width() - $('#slider_handle').width();
initializeElements();
sliderUpdate();
});
</script>
</head>
<body>
<div id="slider">
<div id="slider_bar">
<div id="slider_handle"></div>
</div>
<div id="datapoint_info">
</div>
</div>
<div id="details">
</div>
</body>
</html>
<?php
}

@ -13,11 +13,54 @@
if($_CPHP !== true) { die(); }
class OwnershipException extends Exception {}
class UserAccessException extends Exception {}
class NotFoundException extends Exception {}
class PrototypeException extends Exception {}
class ConstructorException extends Exception {}
class MissingDataException extends Exception {}
class DatabaseException extends Exception {}
class TypeException extends Exception {}
class BaseException extends Exception
{
public function __construct($message, $code = 0, $previous = null, $data = array())
{
$this->data = $data;
parent::__construct($message, $code, $previous);
}
}
class OwnershipException extends BaseException {}
class UserAccessException extends BaseException {}
class PrototypeException extends BaseException {}
class ConstructorException extends BaseException {}
class MissingDataException extends BaseException {}
class DatabaseException extends BaseException {}
class DatabaseDuplicateException extends DatabaseException {}
class DatabaseConstraintException extends DatabaseException {}
class TypeException extends BaseException {}
class DeprecatedException extends BaseException {}
class TemplateException extends Exception
{
public $message = "";
public $file = "";
public $startpos = 0;
public $endpos = 0;
public $code = 0;
public function __construct($message, $file, $startpos, $endpos = 0, $code = "")
{
$this->message = $message;
$this->file = $file;
$this->startpos = $startpos;
$this->endpos = $endpos;
}
}
class NotFoundException extends BaseException
{
public function __construct($message, $code = 0, $previous = null, $field = "", $data = array())
{
$this->field = $field;
parent::__construct($message, $code, $previous, $data);
}
}
class TemplateSyntaxException extends TemplateException {}
class TemplateParsingException extends TemplateException {}
class TemplateEvaluationException extends BaseException {}

@ -0,0 +1,700 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
/* TODO:
* - Freak out if there are invalid CSRF tokens.
* - Let the user choose not to have it freak out if there are invalid CSRF tokens.
*/
if($_CPHP !== true) { die(); }
class FormValidationException extends Exception {
private function DoGetOffendingKeys($exceptions)
{
$results = array();
foreach($exceptions as $exception)
{
if(isset($exception['key']))
{
$results[] = array(
"key" => $exception["key"],
"index" => isset($exception["index"]) ? $exception["index"] : 0
);
}
if(isset($exception["children"]))
{
$results = array_merge($results, $this->DoGetOffendingKeys($exception["children"]));
}
}
return $results;
}
public function __construct($message, $exceptions)
{
$this->message = $message;
$this->exceptions = $exceptions;
}
public function GetErrors()
{
/* We just need to return a flattened version of the exception list here. */
$results = array();
foreach($this->exceptions as $exception_list)
{
$results = array_merge($results, $exception_list);
}
return $results;
}
public function GetErrorMessages($custom_map = array())
{
$flattened = $this->GetErrors();
$results = array();
foreach($flattened as $exception)
{
if(!empty($custom_map) && array_key_exists($exception["error_type"], $custom_map) && array_key_exists($exception["key"], $custom_map[$exception["error_type"]]))
{
/* A custom error message was defined for this particular key/type error combination. */
$results[] = $custom_map[$exception["error_type"]][$exception["key"]];
}
else
{
/* Use default error message. */
$results[] = $exception["error_msg"];
}
}
return $results;
}
public function GetOffendingKeys()
{
$results = array();
foreach($this->exceptions as $exception_list)
{
$results = array_merge($results, $this->DoGetOffendingKeys($exception_list));
}
return $results;
}
}
class ImmediateAbort extends FormValidationException { }
class CPHPFormValidatorPromiseBaseClass
{
public $previous = null;
public $next = null;
public function __construct($creator)
{
$this->previous = $creator;
}
public function StartResolve()
{
/* Back and forth! */
if($this->previous == $this->handler)
{
$this->ContinueResolve(array());
}
else
{
$this->previous->StartResolve();
}
}
public function ContinueResolve($results)
{
$own_result = $this->Resolve($results);
if(is_null($own_result) === false)
{
$results[] = $own_result;
}
if(is_null($this->next) === false)
{
$this->next->ContinueResolve($results);
}
else
{
$this->ValidationFinished($results);
}
}
public function ValidationFinished($results)
{
if(count($results) > 0)
{
throw new FormValidationException("One or more validation steps failed.", $results);
}
}
/* Operators */
public function Either($error_message)
{
$this->next = new CPHPFormValidatorOperatorEither($this, $error_message, array_slice(func_get_args(), 1));
$this->next->handler = $this->handler;
return $this->next;
}
public function All($error_message)
{
$this->next = new CPHPFormValidatorOperatorAll($this, $error_message, array_slice(func_get_args(), 1));
$this->next->handler = $this->handler;
return $this->next;
}
public function Switch_($varname, $error_message)
{
$this->next = new CPHPFormValidatorOperatorSwitch($this, $varname, $error_message, array_slice(func_get_args(), 2));
$this->next->handler = $this->handler;
return $this->next;
}
public function Case_($value)
{
$this->next = new CPHPFormValidatorOperatorCase($this, $value, array_slice(func_get_args(), 1));
$this->next->handler = $this->handler;
return $this->next;
}
/* Special instructions */
public function AbortIfErrors()
{
$this->next = new CPHPFormValidatorAbortIfErrors($this, $this->handler);
$this->next->handler = $this->handler;
return $this->next;
}
public function Done()
{
/* Trigger validation routine */
try
{
$this->StartResolve();
}
catch (ImmediateAbort $e)
{
throw new FormValidationException("A critical validation step failed.", $e->exceptions);
}
}
/* Validators */
public function RequireKey($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "required", "A value is required for this field.", $critical, function($key, $value, $args, $handler){
return isset($handler->formdata[$key]);
});
$this->next->handler = $this->handler;
return $this->next;
}
public function RequireNonEmpty($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "required", "The value for this field must not be empty.", $critical, function($key, $value, $args, $handler){
return trim($value) !== "";
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateEmail($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "email", "The value is not a valid e-mail address.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateNumeric($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "numeric", "The value is not numeric.", $critical, function($key, $value, $args, $handler){
return is_numeric($value) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateUrl($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "url", "The value is not a valid URL.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_URL) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateIp($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "ip", "The value is not a valid IP address.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_IP) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateIpv4($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "ip4", "The value is not a valid IPv4 address.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateIpv6($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "ip6", "The value is not a valid IPv6 address.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidatePublicIp($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "ip_public", "The value is not an IP in a publicly usable range.", $critical, function($key, $value, $args, $handler){
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidatePrivateIp($key, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "ip_private", "The value is not an IP in a private range.", $critical, function($key, $value, $args, $handler){
return (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE) === false && filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) !== false);
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateRegex($key, $error_message, $pattern, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array("pattern" => $pattern), "regex", $error_message, $critical, function($key, $value, $args, $handler){
return preg_match($args["pattern"], $value) === 1;
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateValue($key, $error_message, $values, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array("values" => $values), "value", $error_message, $critical, function($key, $value, $args, $handler){
if(is_array($args["values"]))
{
return in_array($value, $args["values"]);
}
else
{
return ($args["values"] == $value);
}
});
$this->next->handler = $this->handler;
return $this->next;
}
public function ValidateCustom($key, $error_message, $validator, $critical = false)
{
$this->next = new CPHPFormValidatorPromise($this, $this->handler, $key, array(), "custom", $error_message, $critical, $validator);
$this->next->handler = $this->handler;
return $this->next;
}
}
class CPHPFormValidatorPromise extends CPHPFormValidatorPromiseBaseClass
{
public function __construct($creator, $handler, $key, $args, $error_type, $error_message, $critical, $function)
{
parent::__construct($creator);
$this->key = $key;
$this->func = $function;
$this->args = $args;
$this->error_type = $error_type;
$this->error_message = $error_message;
$this->critical = $critical;
$this->handler = $handler;
}
public function Resolve($results)
{
$func = $this->func; /* WTF PHP? Why can't I call $this->func directly? */
$exceptions = array();
$values = isset($this->handler->formdata[$this->key]) ? $this->handler->formdata[$this->key] : null;
if(is_array($values) === true)
{
/* Array */
foreach($values as $i => $value)
{
if($func($this->key, $value, $this->args, $this->handler) !== true)
{
$exceptions[] = array(
"type" => "array_value",
"key" => $this->key,
"index" => $i,
"error_type" => $this->error_type,
"error_msg" => $this->error_message
);
}
}
}
else
{
/* Single value */
if($func($this->key, $values, $this->args, $this->handler) !== true)
{
$exceptions[] = array(
"type" => "single",
"key" => $this->key,
"index" => 0,
"error_type" => $this->error_type,
"error_msg" => $this->error_message
);
}
}
if(count($exceptions) > 0 && $this->critical === true)
{
$results[] = $exceptions;
throw new ImmediateAbort("Critical validation did not pass.", $results);
}
if(count($exceptions) == 0)
{
return null;
}
else
{
return $exceptions;
}
}
}
class CPHPFormValidatorAbortIfErrors extends CPHPFormValidatorPromiseBaseClass
{
public function __construct($creator, $handler)
{
parent::__construct($creator);
$this->handler = $handler;
}
public function Resolve($exceptions)
{
if(count($exceptions) > 0)
{
throw new FormValidationException("One or more validation errors before an AbortIfErrors statement.", $exceptions);
}
else
{
return null;
}
}
}
class CPHPFormValidatorOperator extends CPHPFormValidatorPromiseBaseClass
{
public function __construct($creator, $error_message, $children)
{
parent::__construct($creator);
$this->error_message = $error_message;
$this->children = $children;
}
}
class CPHPFormValidatorOperatorEither extends CPHPFormValidatorOperator
{
public function Resolve($results)
{
$exceptions = array();
foreach($this->children as $child)
{
$result = $child->Resolve($exceptions);
if(is_null($result) === false)
{
$exceptions[] = $result;
}
}
if(count($exceptions) == count($this->children))
{
return array(array(
"type" => "operator",
"error_type" => "either",
"error_msg" => $this->error_message,
"children" => $exceptions
));
}
else
{
return null;
}
}
}
class CPHPFormValidatorOperatorAll extends CPHPFormValidatorOperator
{
public function Resolve($results)
{
$exceptions = array();
foreach($this->children as $child)
{
$result = $child->Resolve($exceptions);
if(is_null($result) === false)
{
$exceptions[] = $result;
}
}
if(count($exceptions) > 0)
{
return array(array(
"type" => "operator",
"error_type" => "both",
"error_msg" => $this->error_message,
"children" => $exceptions
));
}
else
{
return null;
}
}
}
class CPHPFormValidatorOperatorSwitch extends CPHPFormValidatorOperator
{
/* The 'case' operator has a different constructor; it needs to accept both
* an error message, and a variable to check. */
public function __construct($creator, $varname, $error_message, $children)
{
$this->varname = $varname;
parent::__construct($creator, $error_message, $children);
}
public function Resolve($results)
{
$exceptions = array();
foreach($this->children as $child)
{
/* We have to set the variable name in the child here... only at
* runtime can we establish the link between parent and child. */
$child->varname = $this->varname;
$result = $child->Resolve($exceptions);
if(is_null($result) === false)
{
$exceptions[] = $result;
}
}
if(count($exceptions) > 0)
{
return array(array(
"type" => "operator",
"error_type" => "switch",
"error_msg" => $this->error_message,
"children" => $exceptions
));
}
else
{
return null;
}
}
}
class CPHPFormValidatorOperatorCase extends CPHPFormValidatorOperator
{
/* The 'case' operator has a different constructor; instead of an error message,
* it is passed a "trigger value"; that is, the value on which it will execute. */
public function __construct($creator, $value, $children)
{
/* FIXME: Check if the parent really is a Switch operator... */
/* Grab the variable name to check from the parent. */
$this->value = $value;
parent::__construct($creator, "", $children);
}
public function Resolve($results)
{
if(in_array($this->value, $this->handler->GetValues($this->varname)))
{
$exceptions = array();
foreach($this->children as $child)
{
$result = $child->Resolve($exceptions);
if(is_null($result) === false)
{
$exceptions[] = $result;
}
}
if(count($exceptions) > 0)
{
return array(array( /* TODO: Unpack case errors upon handling? Insignificant wrapper.. */
"type" => "operator",
"error_type" => "case",
"error_msg" => "Errors occurred.",
"children" => $exceptions
));
}
else
{
return null;
}
}
else
{
/* Case didn't trigger; always treat as success in that case. */
return null;
}
}
}
class CPHPFormHandler extends CPHPFormValidatorPromiseBaseClass
{
public function __construct($formdata = null, $no_csrf = false)
{
if(is_null($formdata))
{
$this->formdata = $_POST;
}
else
{
$this->formdata = $formdata;
}
$this->no_csrf = $no_csrf;
$this->handler = $this;
$this->validation_exceptions = array();
$this->exception_buffer = array();
$this->first_validation = true;
if($no_csrf === false)
{
CSRF::VerifyToken($this->formdata);
}
}
public function StoreValidationException($exception, $validator_object)
{
if($validator_object == $this)
{
if($this->first_validation === true)
{
$this->first_validation = false;
$this->validation_exceptions[] = $exception;
}
else
{
$this->exception_buffer[] = $exception;
}
}
else
{
$this->validation_exceptions[] = $exception;
}
}
public function RaiseValidationExceptions($aborted)
{
if(count($this->validation_exceptions) > 0)
{
throw new FormValidationException("One or more validation errors occurred.", $this->validation_exceptions);
}
$this->validation_exceptions = array();
}
public function GetGroupedValues()
{
/* Returns an array of associative arrays. This is used for forms that have
* multiple array inputs, and where each input has a corresponding element
* for another input name. */
$keys = func_get_args();
$sCounts = array();
foreach($keys as $key)
{
$sCounts[] = count($this->formdata[$key]);
}
$sTotalItems = max($sCounts);
$sAllValues = array();
for($i = 0; $i < $sTotalItems; $i++)
{
$sValues = array();
foreach($keys as $key)
{
$sValues[$key] = $this->formdata[$key][$i];
}
$sAllValues[] = $sValues;
}
return $sAllValues;
}
public function GetValues($key)
{
/* Returns an array with zero or more values for the given key. If the key
* does not exist, an empty array is returned. */
if(!isset($this->formdata[$key]))
{
return array();
}
elseif(is_array($this->formdata[$key]))
{
return $this->formdata[$key];
}
else
{
return array($this->formdata[$key]);
}
}
public function GetValue($key, $default=null)
{
/* Returns a single value for the given key. If the key contains an array, it
* will return the first element. If the key does not exist, it will return null. */
if(!isset($this->formdata[$key]))
{
return $default;
}
elseif(is_array($this->formdata[$key]))
{
return $this->formdata[$key][0];
}
else
{
return $this->formdata[$key];
}
}
}

@ -0,0 +1,99 @@
<?php
/*
* CPHP is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if($_CPHP !== true) { die(); }
if(!isset($cphp_config->libraries))
{
/* No library configuration has been specified. We don't need to
* execute the rest of this file. */
return;
}
/* Some libraries want to have a cache directory. Instead of storing all
* this stuff in the document root, we'll put it into /tmp (or any other
* config-specified directory). We should probably make sure the needed
* directories exist, though... */
$tmp_dir = (isset($cphp_config->tmp_dir)) ? $cphp_config->tmp_dir : "/tmp";
if(!file_exists("{$tmp_dir}/cphp"))
{
mkdir("{$tmp_dir}/cphp", 0700);
}
if(!file_exists("{$tmp_dir}/cphp/cache"))
{
mkdir("{$tmp_dir}/cphp/cache", 0700);
}
/* We'll set up HTMLPurifier here if so desired. */
if(isset($cphp_config->libraries->htmlpurifier))
{
if(!is_dir("{$tmp_dir}/cphp/cache/htmlpurifier"))
{
mkdir("{$tmp_dir}/cphp/cache/htmlpurifier", 0700);
}
$library_config = $cphp_config->libraries->htmlpurifier;
$library_path = (isset($library_config->path)) ? $library_config->path : "lib/htmlpurifier/HTMLPurifier.auto.php";
require_once($library_path);
$purifier_config = HTMLPurifier_Config::createDefault();
if(isset($library_config->doctype))
{
$purifier_config->set("HTML.Doctype", $library_config->doctype);
}
if(isset($library_config->encoding))
{
$purifier_config->set("Core.Encoding", $library_config->encoding);
}
$purifier_config->set("Cache.SerializerPath", "{$tmp_dir}/cphp/cache/htmlpurifier");
$cphp_purifier = new HTMLPurifier($purifier_config);
$cphp_hash_purifier_config = md5(serialize($purifier_config));
function purify_html($input, $cache_duration = 3600, $config = null)
{
if(isset($config))
{
$config->set("Cache.SerializerPath", "{$tmp_dir}/cphp/cache/htmlpurifier");
$cphp_purifier = new HTMLPurifier($config);
$hash_config = md5(serialize($config));
}
else
{
global $cphp_purifier;
global $cphp_hash_purifier_config;
$hash_config = $cphp_hash_purifier_config;
}
$hash_input = md5($input) . md5($input . "x");
$memcache_key = "purify_{$hash_config}_{$hash_input}";
if($result = mc_get($memcache_key))
{
return $result;
}
else
{
$result = $cphp_purifier->purify($input);
mc_set($memcache_key, $result, $cache_duration);
return $result;
}
}
}

@ -11,9 +11,16 @@
* licensing text.
*/
$cphp_mysql_host = "localhost";
$cphp_mysql_user = "root";
$cphp_mysql_pass = "";
$cphp_mysql_db = "";
if($_CPHP !== true) { die(); }
?>
if(!empty($cphp_config->locale->default_locale))
{
$locale = new Localizer();
$locale->Load($cphp_config->locale->default_locale);
setlocale(LC_ALL, $locale->locale);
}
else
{
die("No default locale was specified. Refer to the CPHP manual for instructions.");
}

@ -13,10 +13,10 @@
if($_CPHP !== true) { die(); }
if($cphp_memcache_enabled)
if(!empty($cphp_config->memcache->enabled))
{
$memcache = new Memcache;
$cphp_memcache_established = $memcache->connect($cphp_memcache_server, $cphp_memcache_port);
$cphp_memcache = new Memcache;
$cphp_memcache_established = @$cphp_memcache->connect($cphp_config->memcache->hostname, $cphp_config->memcache->port);
if($cphp_memcache_established !== false)
{
@ -30,15 +30,16 @@ if($cphp_memcache_enabled)
function mc_get($key)
{
global $cphp_memcache_enabled, $cphp_memcache_connected, $memcache;
global $cphp_config, $cphp_memcache_connected, $cphp_memcache;
if($cphp_memcache_enabled === false || $cphp_memcache_connected === false)
if(empty($cphp_config->memcache->enabled) || $cphp_memcache_connected === false)
{
return false;
}
else
{
$get_result = $memcache->get($key);
$get_result = $cphp_memcache->get($key);
if($get_result !== false)
{
return $get_result;
@ -52,15 +53,15 @@ function mc_get($key)
function mc_set($key, $value, $expiry)
{
global $cphp_memcache_enabled, $cphp_memcache_connected, $cphp_memcache_compressed, $memcache;
global $cphp_config, $cphp_memcache_connected, $cphp_memcache;
if($cphp_memcache_enabled === false || $cphp_memcache_connected === false)
if(empty($cphp_config->memcache->enabled) || $cphp_memcache_connected === false)
{
return false;
}
else
{
if($cphp_memcache_compressed === true)
if(!empty($cphp_config->memcache->compressed) === true)
{
$flag = MEMCACHE_COMPRESSED;
}
@ -69,66 +70,29 @@ function mc_set($key, $value, $expiry)
$flag = false;
}
$set_result = $memcache->set($key, $value, $flag, $expiry);
$set_result = $cphp_memcache->set($key, $value, $flag, $expiry);
return $set_result;
}
}
function mc_delete($key)
{
global $cphp_memcache_enabled, $cphp_memcache_connected, $memcache;
global $cphp_config, $cphp_memcache_connected, $cphp_memcache;
if($cphp_memcache_enabled === false || $cphp_memcache_connected === false)
if(empty($cphp_config->memcache->enabled) || $cphp_memcache_connected === false)
{
return false;
}
else
{
return $memcache->delete($key);
$delete_result = $cphp_memcache->delete($key);
return $delete_result;
}
}
function mysql_query_cached($query, $expiry = 60, $key = "")
function mysql_query_cached($query, $expiry = 60, $key = "", $exec = false)
{
if($key == "")
{
$key = md5($query) . md5($query . "x");
}
if($res = mc_get($key))
{
$return_object->source = "memcache";
$return_object->data = $res;
return $return_object;
}
else
{
if($res = mysql_query($query))
{
$found = false;
while($row = mysql_fetch_assoc($res))
{
$return_object->data[] = $row;
$found = true;
}
if($found === true)
{
$return_object->source = "database";
mc_set($key, $return_object->data, $expiry);
return $return_object;
}
else
{
return false;
}
}
else
{
return false;
}
}
throw new DeprecatedException("All mysql_* functionality in CPHP has been removed. Use CachedPDO syntax instead.");
}
function file_get_contents_cached($path, $expiry = 3600)

@ -13,14 +13,79 @@
if($_CPHP !== true) { die(); }
function random_string($length)
function random_string($length, $insecure = false)
{
/* This was changed to return cryptographically secure
* random strings by default. The $insecure parameter
* can be used to indicate that cryptographically insecure
* (but faster) random data is desired. Note that, absent
* a source for cryptographically secure random data,
* this function will raise an exception. */
$required_bytes = ceil($length / 4) * 3; /* Accounting for base64 overhead */
$bytes = random_bytes($required_bytes, $insecure);
/* Since we want a string, we will base64-encode the
* resulting bytes, and then replace + and / with - and _
* respectively. We'll also cut down on the amount of
* bytes - since we're requesting bytes in chunks of 4,
* we might have a few too many. */
return substr(str_replace("+", "-", str_replace("/", "_", base64_encode($bytes))), 0, $length);
}
function random_bytes($length, $insecure = false)
{
if($insecure === true)
{
$output = "";
for ($i = 0; $i < $length; $i++)
{
$output .= substr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", mt_rand(0, 61), 1);
$output .= chr(mt_rand(0, 255));
}
return $output;
}
else
{
$success = false;
/* Prefer /dev/urandom if it is available. */
if(file_exists("/dev/urandom"))
{
$handle = fopen("/dev/urandom", "r");
$bytes = fread($handle, $length);
$success = true;
}
/* Fall back to openssl_random_pseudo_bytes if it is available. */
if($success === false && function_exists("openssl_random_pseudo_bytes"))
{
$crypto_secure = false;
$bytes = openssl_random_pseudo_bytes($length, $crypto_secure);
if($crypto_secure === true)
{
$success = true;
}
}
/* If we also don't have that, let's try mcrypt_create_iv... but only if
* we are running on PHP 5.3, since it behaves unpredictably on
* Windows in lower versions. */
if($success === false && function_exists("mcrypt_create_iv") && version_compare(phpversion(), "5.3", ">="))
{
$bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
$success = true;
}
/* There are no secure random data sources available. Fix your
* system! */
if($success === false)
{
throw new Exception("No secure sources for random bytes found. Install the OpenSSL or mcrypt extension.");
}
return $bytes;
}
}
function extract_globals()
@ -114,7 +179,293 @@ function pretty_dump($input)
echo($output);
}
/*function is_empty($variable)
function rgb_from_hex($hex)
{
if(strlen($hex) == 6)
{
$r = substr($hex, 0, 2);
$g = substr($hex, 2, 2);
$b = substr($hex, 4, 2);
$rgb['r'] = base_convert($r, 16, 10);
$rgb['g'] = base_convert($g, 16, 10);
$rgb['b'] = base_convert($b, 16, 10);
return $rgb;
}
else
{
return false;
}
}
function hex_from_rgb($rgb)
{
if(!empty($rgb['r']) && !empty($rgb['g']) && !empty($rgb['b']))
{
return base_convert($rgb['r'], 10, 16) . base_convert($rgb['g'], 10, 16) . base_convert($rgb['b'], 10, 16);
}
else
{
return false;
}
}
function strip_tags_attr($string, $allowtags = NULL, $allowattributes = NULL)
{
/* Thanks to nauthiz693@gmail.com (http://www.php.net/manual/en/function.strip-tags.php#91498) */
$string = strip_tags($string,$allowtags);
if (!is_null($allowattributes))
{
if(!is_array($allowattributes))
{
$allowattributes = explode(",",$allowattributes);
}
if(is_array($allowattributes))
{
$allowattributes = implode(")(?<!",$allowattributes);
}
if (strlen($allowattributes) > 0)
{
$allowattributes = "(?<!".$allowattributes.")";
}
$string = preg_replace_callback("/<[^>]*>/i",create_function('$matches', 'return preg_replace("/ [^ =]*'.$allowattributes.'=(\"[^\"]*\"|\'[^\']*\')/i", "", $matches[0]);'),$string);
}
return $string;
}
function cut_text($input, $length)
{
if(strlen($input) > $length)
{
if(preg_match("/^(.{0,{$length}})\W/s", $input, $matches))
{
return $matches[1] . "...";
}
else
{
return "";
}
}
else
{
return $input;
}
}
function filter_html($input)
{
return strip_tags_attr($input, "<a><b><i><u><span><div><p><img><br><hr><font><ul><li><ol><dt><dd><h1><h2><h3><h4><h5><h6><h7><del><map><area><strong><em><big><small><sub><sup><ins><pre><blockquote><cite><q><center><marquee><table><tr><td><th>", "href,src,alt,class,style,align,valign,color,face,size,width,height,shape,coords,target,border,cellpadding,cellspacing,colspan,rowspan");
}
function filter_html_strict($input)
{
return strip_tags_attr($input, "<strong><em><br><hr><img><a><span><p><div>", "src,href,style");
}
function parse_rss($url)
{
$rss = new DOMDocument();
$rss->load($url);
$items = array();
foreach($rss->getElementsByTagName('item') as $item)
{
$items[] = array(
'title' => $item->getElementsByTagName('title')->item(0)->nodeValue,
'description' => $item->getElementsByTagName('description')->item(0)->nodeValue,
'url' => $item->getElementsByTagName('link')->item(0)->nodeValue,
'date' => strtotime($item->getElementsByTagName('pubDate')->item(0)->nodeValue)
);
}
return $items;
}
function fix_utf8($input)
{
return utf8_encode(utf8_decode($input));
}
function generate_pagination($min, $max, $current, $around, $start, $end)
{
/* Generates an array with pages that should be shown in a pagination bar.
* $min The first page number (this will usually be 1).
* $max The last page number (this is usually the total amount of pages).
* $current The page the user is currently on.
* $around The amount of pages that should at least be shown around the current page.
* $start The amount of pages that should at least be shown at the start.
* $end The amount of pages that should at least be shown at the end.
*
* Returns:
* Array, containing integers (for the pages) and null objects (for the boundaries). */
if($max < ($start + $end + ($around * 2) + 1))
{
/* There are less pages than there would be elements. */
$return_array = array();
for($i = 1; $i <= $max; $i++)
{
$return_array[] = $i;
}
return $return_array;
}
else
{
/* Calculation time... */
$return_array = array();
/* Start with the left segment. */
for($i = 1; $i <= $start; $i++)
{
$return_array[] = $i;
}
/* Now the middle segment. */
if($start + $around >= $current - 1)
{
$left_start = $i;
}
else
{
$return_array[] = null;
$left_start = $current - $around;
}
for($i = $left_start; $i <= $current + $around; $i++)
{
if($i >= $max)
{
break;
}
$return_array[] = $i;
}
/* And finally the right segment. */
if($max - ($end + $around) <= $current + 1)
{
$right_start = $i;
}
else
{
$return_array[] = null;
$right_start = $max - $end;
}
for($i = $right_start; $i <= $max; $i++)
{
$return_array[] = $i;
}
/* All done! */
return $return_array;
}
}
function starts_with($haystack, $needle)
{
return (substr($haystack, 0, strlen($needle)) == $needle);
}
function ends_with($haystack, $needle)
{
return (substr($haystack, -strlen($needle)) == $needle);
}
function redirect($target)
{
header("Location: {$target}");
die();
}
function ceil_precision($value, $precision = 0)
{
$multiplier = pow(10, $precision);
return ceil($value * $multiplier) / $multiplier;
}
function str_lreplace($search, $replace, $subject)
{
$pos = strrpos($subject, $search);
if($pos !== false)
{
$subject = substr_replace($subject, $replace, $pos, strlen($search));
}
return $subject;
}
function http_status_code($code)
{
return (trim($variable) == "");
}*/
$codes = array(
100 => "Continue",
101 => "Switching Protocols",
200 => "OK",
201 => "Created",
202 => "Accepted",
203 => "Non-Authoritative Information",
204 => "No Content",
205 => "Reset Content",
206 => "Partial Content",
300 => "Multiple Choices",
301 => "Moved Permanently",
302 => "Moved Temporarily",
303 => "See Other",
304 => "Not Modified",
305 => "Use Proxy",
400 => "Bad Request",
401 => "Unauthorized",
402 => "Payment Required",
403 => "Forbidden",
404 => "Not Found",
405 => "Method Not Allowed",
406 => "Not Acceptable",
407 => "Proxy Authentication Required",
408 => "Request Time-out",
409 => "Conflict",
410 => "Gone",
411 => "Length Required",
412 => "Precondition Failed",
413 => "Request Entity Too Large",
414 => "Request-URI Too Large",
415 => "Unsupported Media Type",
418 => "I'm a teapot",
422 => "Unprocessable Entity",
500 => "Internal Server Error",
501 => "Not Implemented",
502 => "Bad Gateway",
503 => "Service Unavailable",
504 => "Gateway Time-out",
505 => "HTTP Version not supported",
);
if(array_key_exists($code, $codes))
{
$text = $codes[$code];
}
else
{
throw new Exception("The specified HTTP status code does not exist.");
}
if(strpos(php_sapi_name(), "cgi") !== false)
{
header("Status: {$code} {$text}");
}
else
{
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header("{$protocol} {$code} {$text}");
}
}

@ -15,13 +15,161 @@ if($_CPHP !== true) { die(); }
$cphp_mysql_connected = false;
if($cphp_mysql_enabled === true)
class CachedPDO extends PDO
{
if(mysql_connect($cphp_mysql_host, $cphp_mysql_user, $cphp_mysql_pass))
public function CachedQuery($query, $parameters = array(), $expiry = 60)
{
if(mysql_select_db($cphp_mysql_db))
/* TODO: Do type guessing before checking cache, so as to avoid
* different parameter hashes depending on input type for
* numbers. */
$query_hash = md5($query);
$parameter_hash = md5(serialize($parameters));
$cache_hash = $query_hash . $parameter_hash;
$return_object = new stdClass;
if($expiry != 0 && $result = mc_get($cache_hash))
{
$return_object->source = "memcache";
$return_object->data = $result;
}
else
{
$statement = $this->prepare($query);
if(count($parameters) > 0)
{
foreach($parameters as $key => $value)
{
$type = $this->GuessType($value);
if(preg_match("/^[0-9]+$/", $value) && is_string($value))
{
/* PDO library apparently thinks it's part of a strongly typed language and doesn't do any typecasting.
* We'll do it ourselves then. */
$int_value = (int) $value;
if($int_value < PHP_INT_MAX)
{
/* We only want to cast to integer if the result doesn't exceed INT_MAX, to avoid overflows. The
* only way to do this appears to be aborting when it *equals* or exceeds INT_MAX, as an overflow
* would occur during this check also. */
$value = $int_value;
$type = PDO::PARAM_INT;
}
}
if($type == PDO::PARAM_STR)
{
$value = strval($value);
}
$statement->bindValue($key, $value, $type);
}
}
if($statement->execute() === true)
{
if($result = $statement->fetchAll(PDO::FETCH_ASSOC))
{
if(count($result) > 0)
{
if($expiry != 0)
{
mc_set($cache_hash, $result, $expiry);
}
$return_object->source = "database";
$return_object->data = $result;
}
else
{
return false;
}
}
else
{
$last_id = $this->lastInsertId();
if($last_id == "0" || !starts_with(strtoupper($query), "INSERT"))
{
/* There were zero results. Return null instead of an object without results, to allow for statements
* of the form if($result = $database->CachedQuery()) . */
return null;
}
else
{
/* This was an INSERT query. Return the primary ID of the created row. */
return $last_id;
}
}
}
else
{
/* The query failed. */
$err = $statement->errorInfo();
if($err[0] == "23000")
{
if(starts_with($err[2], "Duplicate entry")) /* There does not seem to be a better way of doing this. */
{
throw new DatabaseDuplicateException("The query failed because one of the keys was not unique: {$err[2]}", 0, null, array('query' => $query, 'parameters' => $parameters));
}
else
{
throw new DatabaseConstraintException("The query violates a database constraint: {$err[2]}", 0, null, array('query' => $query, 'parameters' => $parameters));
}
}
else
{
throw new DatabaseException("The query failed: {$err[2]}", 0, null, array('query' => $query, 'parameters' => $parameters));
}
}
}
return $return_object;
}
public function GuessType($value)
{
if(is_object($value))
{
throw new DatabaseException("Query parameters must be numeric, boolean, null, a string value, or something that can be auto-cast to a string. You provided an object.");
}
if(is_int($value))
{
return PDO::PARAM_INT;
}
elseif(is_bool($value))
{
return PDO::PARAM_BOOL;
}
elseif(is_null($value))
{
return PDO::PARAM_NULL;
}
else
{
return PDO::PARAM_STR;
}
}
}
if(!empty($cphp_config->database->driver))
{
if(empty($cphp_config->database->database))
{
die("No database was configured. Refer to the CPHP manual for instructions.");
}
try
{
$database = new CachedPDO("mysql:host={$cphp_config->database->hostname};dbname={$cphp_config->database->database}", $cphp_config->database->username, $cphp_config->database->password);
$database->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING);
$cphp_mysql_connected = true;
}
catch (Exception $e)
{
die("Could not connect to the specified database. Refer to the CPHP manual for instructions.");
}
}

Loading…
Cancel
Save