00001 <?php
00016 if(!defined('CHARSET')) define('CHARSET', 'UTF-8');
00017
00018 if(!defined('SLASHES')) define('SLASHES', '\\/');
00019 if(!defined('WDS')) define('WDS', '/');
00020 if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
00021
00022
00023 class Web {
00024 var $context;
00025 var $controller;
00026
00027
00028 var $start_point;
00029 var $time_start;
00030
00031
00032 var $debug_messages;
00033 var $no_debug = false;
00034
00035
00036
00037 var $q_html_head;
00038 var $q_css_load;
00039 var $q_js_load;
00040 var $q_js_run;
00041
00042
00043
00044
00045
00046
00047 function Web($start_point)
00048 {
00049 $this->time_start = array_sum(explode(" ",microtime()));
00050 $this->start_point = $start_point;
00051 $this->ajax = false;
00052 $this->q_html_head = array();
00053 $this->q_css_load = array();
00054 $this->q_js_load = array();
00055 $this->q_js_run = array();
00056
00057 $this->debug_messages = array();
00058
00059 $this->context->protocol = isset($_SERVER['HTTPS']) ? 'https' : 'http';
00060 $this->context->host = $_SERVER['SERVER_NAME'];
00061 $this->context->port = $_SERVER['SERVER_PORT'];
00062 $this->context->basedir = dirname($start_point);
00063 $this->context->home = $this->context->basedir;
00064 $this->context->method = $_SERVER['REQUEST_METHOD'];
00065 $this->context->fullpath = $_SERVER['REQUEST_URI'];
00066 $this->context->ip = $_SERVER['REMOTE_ADDR'];
00067
00068 if(defined('DIR_WS_BASE')) {
00069 $path = WDS.DIR_WS_BASE.WDS;
00070 } else {
00071
00072 $len = strlen($_SERVER['DOCUMENT_ROOT']);
00073 $path = str_replace('\\', WDS, $this->context->basedir);
00074 $path = WDS.trim(substr($path, $len), WDS).WDS;
00075 }
00076 $this->context->basepath = preg_replace("/(\/+)/", "/", $path);
00077 if($this->context->basepath == '') {
00078 $this->context->basepath = '/';
00079 }
00080
00081 if(!defined('BASE_URL')) define('BASE_URL', $this->context->basepath);
00082 if(!defined('BASE_DIR')) define('BASE_DIR', $this->context->basedir);
00083
00084 $basepath = $this->context->basepath == WDS ? '' : $this->context->basepath;
00085 $full_url = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $this->context->fullpath;
00086 $url_parts = @parse_url($full_url);
00087 $this->context->path = ltrim(substr($url_parts['path'], strlen($basepath)), WDS);
00088 $this->context->querystring = $url_parts['query'];
00089 }
00090
00091
00092
00093
00094
00095
00096
00102 function enable_debug()
00103 {
00104 $this->queue_css_load('debug', url('/css/debug.css', true));
00105 $this->queue_js_load('debug', url('/js/debug.js', true));
00106 ob_start();
00107 }
00108
00109
00110
00111
00112
00113
00114
00122 function run($urls, $path=false)
00123 {
00124 if($path !== false) {
00125 $this->context->path = ltrim($path, WDS);
00126 }
00127
00128 $uri_parts = explode(WDS, trim($this->context->path, WDS));
00129 $strip_array = create_function('&$array', 'foreach ($array as $key => $item) if ($item === "") unset($array[$key]);');
00130
00131 $possible_classes = array();
00132 foreach($urls as $class_name) {
00133 if(is_array($class_name)) {
00134 $possible_classes[] = $class_name[0];
00135 } else {
00136 $possible_classes[] = $class_name;
00137 }
00138 }
00139
00140 if(defined('DISPATCH_URL')) {
00141
00142 array_shift($uri_parts);
00143 }
00144 $path = str_replace('//', '/', WDS.join(WDS, $uri_parts).WDS);
00145 if ($path == '') {
00146 $path = '/';
00147 }
00148
00149 foreach($urls as $re=>$class_name) {
00150 $action = array();
00151 $params = array();
00152 $re = sprintf('|^%s$|', $re);
00153
00154 if(is_array($class_name)) {
00155 if(@is_array($class_name[2])) {
00156 foreach($class_name[2] as $k=>$v) {
00157 $params[$k] = $v;
00158 }
00159 }
00160 $action = array($class_name[1]);
00161 $class_name = $class_name[0];
00162 }
00163
00164 if(preg_match($re, $path, $matches)) {
00165 array_shift($matches);
00166 $url_args = array();
00167 foreach($matches as $k=>$v) {
00168 if(!is_numeric($k)) {
00169 $url_args[$k] = $v;
00170 unset($matches[$k]);
00171 }
00172 }
00173 $actionarg = '';
00174 if(count($matches) > count($url_args)) {
00175 $actionarg = array_pop($matches);
00176 }
00177
00178 $action_params = array_filter(explode('/', $actionarg), create_function('$a','return !empty($a);'));
00179 if(empty($action)) {
00180 $action = $action_params;
00181 } else {
00182 $action = array_merge($action, $action_params);
00183 }
00184 break;
00185 } else {
00186 $class_name = '';
00187 $action = array();
00188 }
00189 }
00190 if(empty($class_name)) {
00191 error("No matching page controller for path: $path");
00192 $this->notfound();
00193 }
00194
00195
00196 if(substr($class_name, 0, 1) == '>') {
00197 $redir_url = trim(substr($class_name, 1));
00198 $this->redirect($redir_url);
00199 return;
00200 }
00201
00202 define('CURRENT_URL', $this->current_url(true));
00203
00204
00205 $request_args = array_merge($url_args, $_GET, $_POST, $params);
00206 Registry::set('pronto:request_args', $request_args);
00207
00208 $class =& Factory::page($class_name);
00209 if($class === false) {
00210 error("Class ($class_name) for REQUEST_URI not found. Check if you declared this class.");
00211 return $this->notfound();
00212 }
00213
00214 $strip_array($uri_parts);
00215
00216
00217 $wildcard_actions = array();
00218 $last = $this->context->method.'_';
00219 $args = array();
00220 for($i = 0; $i < count($action); $i++) {
00221 $last = $last . $action[$i] . '__';
00222 $args = array_slice($action, $i+1);
00223 $wildcard_actions[] = array($last, $args);
00224 }
00225 $wildcard_actions = array_reverse($wildcard_actions);
00226
00227
00228
00229 $oldaction = $action;
00230 $action = implode('__', $action);
00231
00232
00233
00234
00235 $methods = array_merge(
00236 array(array($this->context->method.'_'.$action, array())),
00237 $wildcard_actions,
00238 array(array($this->context->method, $oldaction))
00239 );
00240 if(empty($action)) {
00241
00242 array_shift($methods);
00243 }
00244
00245
00246
00247 if($this->context->method == 'HEAD') {
00248 $methods_get = $methods;
00249 array_walk($methods_get, create_function('&$v,$k','$v[0] = preg_replace("|^HEAD|", "GET", $v[0]);'));
00250 $methods = array_merge($methods, $methods_get);
00251 }
00252
00253
00254 $method_name = '';
00255 $method_args = array();
00256 foreach($methods as $m) {
00257 if(method_exists($class, $m[0])) {
00258 $method_name = $m[0];
00259 $method_args = $m[1];
00260 break;
00261 }
00262 }
00263 if(!$method_name) {
00264 error("Method <code>{$methods[0]}</code> not found. Check if you declared this method in the class <code>{$class_name}</code>.");
00265 return $this->notfound();
00266 }
00267
00268 define('CONTROLLER', $class_name);
00269 define('ACTION', $method_name);
00270
00271 $this->controller =& $class;
00272 $this->ajax = $this->controller->ajax;
00273
00274
00275 if(function_exists('pronto_init')) {
00276 call_user_func('pronto_init');
00277 }
00278 if(method_exists($class, '__init__')) {
00279 call_user_func(array(&$class, '__init__'));
00280 }
00281
00282
00283 if(!defined('OUTPUT_BUFFERING') || OUTPUT_BUFFERING !== false) ob_start();
00284
00285
00286 call_user_func_array(array($class, $method_name), $method_args);
00287
00288
00289 $this->finish();
00290 }
00291
00302 function finish()
00303 {
00304 if(method_exists($this->controller, '__end__')) {
00305 call_user_func(array(&$this->controller, '__end__'));
00306 }
00307 if(function_exists('pronto_end')) {
00308 call_user_func('pronto_end');
00309 }
00310
00311
00312
00313 if(!empty($_SESSION) && !session_id()) {
00314 $s = $_SESSION;
00315 start_session();
00316 $_SESSION = $s;
00317
00318 $this->expires(-3600);
00319 }
00320
00321
00322 if(ob_get_level()) ob_end_flush();
00323
00324
00325
00326
00327 if(DEBUG === true && !$this->ajax && !$this->no_debug) {
00328 echo '<div id="pronto_debug">';
00329 if(count($this->debug_messages)) {
00330 echo '<h2>Debug Messages</h2>';
00331 foreach($this->debug_messages as $v) {
00332 if($v['msg']) echo "<h4>{$v['loc']}</h4><pre>{$v['msg']}</pre>";
00333 }
00334 }
00335 $db =& Registry::get('pronto:db:main');
00336 if($db && $db->profile === true) {
00337 echo '<h2>Summary</h2>';
00338 echo '<table class="query_profile" cellspacing="0">';
00339 foreach($db->profile_data as $i=>$a) {
00340 $t = $a['time'].' ms';
00341 echo '<tr><th>'.htmlspecialchars($a['query'])."</th><td>$t</td></tr>";
00342 }
00343 echo '<tr><th><b>Total Queries</b></th><td><b>'.count($db->profile_data).'</b></td></tr>';
00344 $total = round((array_sum(explode(" ",microtime()))-$this->time_start)*1000, 3).' ms';
00345 echo "<tr><th><b>Total Execution Time (PHP+SQL)</b></th><td><b>$total</b></td></tr>";
00346 if(function_exists('memory_get_usage')) {
00347 $mem = sprintf("%.2f", memory_get_usage()/1024);
00348 echo "<tr><th><b>Memory Usage</b></th><td><b>$mem KB</b></td></tr>";
00349 }
00350 echo "</table>";
00351 }
00352 echo '</div>';
00353 echo '<div id="pronto_debug_bar">DEBUG</div>';
00354 }
00355
00356
00357 die;
00358 }
00359
00367 function current_url($trim_querystring=false)
00368 {
00369 $path = $this->context->path;
00370 if(defined('DISPATCH_URL')) {
00371 $path = substr($path, strlen(DISPATCH_URL));
00372 }
00373 $url = '/'.$path;
00374 if($this->context->querystring && !$trim_querystring) {
00375 $url .= '?'.$this->context->querystring;
00376 }
00377 return $url;
00378 }
00379
00385 function redirect($url)
00386 {
00387
00388
00389
00390
00391
00392 if(substr($url, 0, 2) == '//') $url = url(substr($url, 1));
00393 $this->header("Location", $url);
00394 }
00395
00396
00397
00398
00399
00400
00401
00414 function check_access($key, $login_url='', $return_url='', $always_login=false)
00415 {
00416 $access =& Registry::get('pronto:access');
00417 if(!$login_url) $login_url = url('/login');
00418 if(!$access->has_access($key)) {
00419 if($access->logged_in && !$always_login) {
00420 $this->forbidden();
00421 } else {
00422 if($return_url !== false) {
00423 if($return_url === '') {
00424 $return_url = $this->current_url();
00425 }
00426 $login_url .= '?return_url='.urlencode($return_url);
00427 }
00428 if($this->ajax) {
00429 $login_url .= '&_ajax=1';
00430 }
00431 $this->redirect($login_url);
00432 }
00433 exit;
00434 }
00435 }
00436
00442 function has_access($key)
00443 {
00444 $access =& Registry::get('pronto:access');
00445 return $access->has_access($key);
00446 }
00447
00455 function http_auth($realm)
00456 {
00457 header('WWW-Authenticate: Basic realm="'.$realm.'"');
00458 header('HTTP/1.0 401 Unauthorized');
00459 echo "You are not authorized to view this page.";
00460 die;
00461 }
00462
00467 function require_https()
00468 {
00469 if(isset($_SERVER['HTTPS'])) return;
00470 $url = absolute_url($this->current_url());
00471 $this->redirect(str_replace('http://', 'https://', $url));
00472 die;
00473 }
00474
00475
00476
00477
00478
00479
00480
00487 function queue_html_head($key, $val)
00488 {
00489 $this->q_html_head[$key] = $val;
00490 }
00491
00492 function queue_css_load($key, $url)
00493 {
00494 if(!isset($this->q_css_load[$key])) {
00495 $this->q_css_load[$key] = $url;
00496 }
00497 }
00498
00514 function queue_js_run($key, $code, $overwrite=true)
00515 {
00516
00517 $key = str_replace(array('/','.'), '__', $key);
00518 if(empty($key)) $key = 'a'.md5($code);
00519 if($overwrite) {
00520 $this->q_js_run[$key] = $code;
00521 } else {
00522 $this->q_js_run[$key] .= "\n".$code;
00523 }
00524 }
00525
00533 function queue_js_load($key, $url)
00534 {
00535
00536 $key = str_replace(array('/','.'), '__', $key);
00537 if(!isset($this->q_js_load[$key])) {
00538 $this->q_js_load[$key] = $url;
00539 }
00540 }
00541
00542
00543
00544
00545
00546
00547
00548
00552 function ajax_load_queues()
00553 {
00554 $js = '';
00555 foreach($this->q_css_load as $k=>$v) {
00556 $js .= 'var f = document.createElement("link");';
00557 $js .= 'f.setAttribute("rel", "stylesheet");';
00558 $js .= 'f.setAttribute("type", "text/css");';
00559 $js .= 'f.setAttribute("href", "'.$v.'");';
00560 $js .= 'document.getElementsByTagName("head").item(0).appendChild(f);'."\n";
00561 }
00562
00563
00564
00565 $js .= "pronto_js_load_queue = new Object();\n";
00566 $js .= "pronto_js_run_queue = new Object();\n";
00567 $js .= "function pronto_run_queue() { var l=0; for(var x in pronto_js_load_queue) l++; if(l==0) { for(var y in pronto_js_run_queue) { eval('pronto_js_run_queue.'+y+'();'); } } }\n";
00568 foreach($this->q_js_run as $k=>$v) {
00569 if(substr($k, 0, 1) == '+') $k = substr($k, 1);
00570
00571 $k = str_replace(array(':','/','-','.'), '_', $k);
00572 $js .= "pronto_js_run_queue.$k = function(){ $v }\n";
00573 }
00574 foreach($this->q_js_load as $k=>$v) {
00575 $js .= "pronto_js_load_queue.$k = true;\n";
00576 $k = str_replace(array(':','/','-','.'), '_', $k);
00577 $js .= "$.getScript(\"$v\", function(){ delete pronto_js_load_queue.$k; pronto_run_queue(); });\n";
00578 }
00579
00580 if(empty($this->q_js_load)) {
00581 $js .= "pronto_run_queue();";
00582 }
00583 return $js;
00584 }
00585
00593 function ajax_render($template, $filename, $jsvars=array())
00594 {
00595 $html = $filename ? $template->fetch($filename) : '';
00596 $json = array(
00597 'js' => $this->ajax_load_queues(),
00598 'html' => $html
00599 );
00600 if(isset($jsvars['redirect_url'])) {
00601
00602 $json['exec'] = 'window.location.href="'.$jsvars['redirect_url'].'";';
00603 } else if(isset($_SESSION['_FLASH_MESSAGE'])) {
00604 $json['flash'] = $_SESSION['_FLASH_MESSAGE'];
00605 unset($_SESSION['_FLASH_MESSAGE']);
00606 }
00607
00608 foreach($jsvars as $k=>$v) $json[$k] = $v;
00609 echo json_encode($json);
00610 }
00611
00617 function ajax_exec($js='')
00618 {
00619 $this->ajax_render(new Template(), '', array('exec'=>$js));
00620 }
00621
00630 function render($template, $filename, $vars=array(), $layout='')
00631 {
00632 if($layout) {
00633 $vars['CONTENT_FOR_LAYOUT'] = $template->fetch($filename, $vars);
00634
00635 $filename = DIR_FS_APP.DS.'templates'.DS.$layout;
00636 }
00637 if(isset($_SESSION['_FLASH_MESSAGE'])) {
00638 $vars['FLASH_MESSAGE'] = $_SESSION['_FLASH_MESSAGE'];
00639 unset($_SESSION['_FLASH_MESSAGE']);
00640 }
00641
00642
00643 $head = '';
00644 foreach($this->q_html_head as $k=>$v) $head .= $v;
00645 if(!$this->ajax) {
00646 foreach($this->q_css_load as $k=>$v) {
00647 $head .= '<link rel="stylesheet" type="text/css" href="'.$v.'" />'."\n";
00648 }
00649 foreach($this->q_js_load as $k=>$v) {
00650 $head .= '<script type="text/javascript" src="'.$v.'"></script>';
00651 }
00652 if(!empty($this->q_js_run)) {
00653 $head .= '<script type="text/javascript">'."\n";
00654 foreach($this->q_js_run as $k=>$v) {
00655 if(substr($k, 0, 1) == '+') {
00656 $head .= "$v\n";
00657 } else {
00658 $head .= '$(document).ready(function(){'.$v.'});'."\n";
00659 }
00660 }
00661 $head .= "</script>\n";
00662 }
00663 }
00664 $vars['HTML_HEAD'] = $head;
00665
00666 echo $template->fetch($filename, $vars);
00667 }
00668
00669
00670
00671
00672
00673
00674
00683 function header($name, $value)
00684 {
00685 header($name.': '.$value);
00686 }
00687
00694 function expires($secs=0)
00695 {
00696 if($secs <= 0) {
00697 $this->header('Cache-Control', "no-cache");
00698 $this->header('Pragma', "no-cache");
00699 } else {
00700 $this->header('Cache-Control', "public,max-age=$secs");
00701 $this->header('Pragma', "");
00702 }
00703 $time = strtotime(gmdate('D, d M Y H:i:s')) + $secs;
00704 $this->header('Expires', gmdate('D, d M Y H:i:s', $time).' GMT');
00705 }
00706
00712 function content_type($type='')
00713 {
00714 if(empty($type)) $type = 'text/html; charset='.CHARSET;
00715 $this->header('Content-Type', $type);
00716 }
00717
00723 function lastmodified($datetime)
00724 {
00725 $this->header('Last-Modified', $datetime);
00726 }
00727
00733 function status($status)
00734 {
00735 $this->context->status = $status;
00736 header("{$_SERVER['SERVER_PROTOCOL']} $status");
00737 }
00738
00743 function notfound()
00744 {
00745 $this->status('404 Not Found');
00746
00747 $p = DIR_FS_APP.DS.'pages'.DS.'404.php';
00748 if(file_exists($p)) require_once($p);
00749
00750 if(function_exists('notfound')) {
00751 call_user_func('notfound', $this);
00752 } else {
00753 echo 'File Not Found';
00754 }
00755 }
00756
00761 function forbidden()
00762 {
00763 $this->status('403 Forbidden');
00764
00765 $p = DIR_FS_APP.DS.'pages'.DS.'404.php';
00766 if(file_exists($p)) require_once($p);
00767
00768 if(function_exists('forbidden')) {
00769 call_user_func('forbidden', $this);
00770 } else {
00771 echo 'Forbidden';
00772 }
00773 }
00774
00779 function badrequest()
00780 {
00781 $this->status('400 Bad Request');
00782
00783 $p = DIR_FS_APP.DS.'pages'.DS.'404.php';
00784 if(file_exists($p)) require_once($p);
00785
00786 if(function_exists('badrequest')) {
00787 call_user_func('badrequest', $this);
00788 } else {
00789 echo 'Bad Request';
00790 }
00791 }
00792
00797 function internalerror()
00798 {
00799 $this->status('500 Internal Server Error');
00800
00801 $p = DIR_FS_APP.DS.'pages'.DS.'404.php';
00802 if(file_exists($p)) require_once($p);
00803
00804 if(function_exists('internalerror')) {
00805 call_user_func('internalerror', $this);
00806 } else {
00807 echo 'Internal Server Error';
00808 }
00809 }
00810 }
00811
00812 ?>