/** * Call this from Elefant's bootstrap.php file so that * links to `/{app_url}/*` map to `/saasy/*`. * * Usage: * * saasy\App::bootstrap ($controller); * * @param \Controller $controller */ public static function bootstrap($controller) { self::$controller = $controller; $conf = self::conf(); $alias = $conf['App Settings']['app_alias']; // Rewrite /app_alias/ to /saasy/ if ($_SERVER['REQUEST_URI'] === '/' . $alias) { $_SERVER['REQUEST_URI'] = '/saasy'; } elseif (strpos($_SERVER['REQUEST_URI'], '/' . $alias . '/') === 0) { $_SERVER['REQUEST_URI'] = str_replace('/' . $alias . '/', '/saasy/', $_SERVER['REQUEST_URI']); } // Add bootstrap.js $page = $controller->page(); $page->add_script('/apps/saasy/bootstrap/js/bootstrap.min.js'); $page->add_script('<script>$(function(){$("input[type=submit]").addClass("btn");});</script>'); // Get the customer from the subdomain $sub = self::subdomain(); if ($sub) { /** @var $customer Customer */ $customer = Customer::query()->where('subdomain', $sub)->single(); if ($customer && !$customer->error) { self::customer($customer); // Get the account from the user if (\User::require_login()) { /** @var $acct Account */ $acct = Account::query()->where('user', \User::val('id'))->where('customer', $customer->id)->single(); if ($acct && !$acct->error) { self::acct($acct); } } } } }
/** * The front controller only has one static method, `run()`, which * */ public static function run ($argv, $argc) { /** * For compatibility with PHP 5.4's built-in web server, we bypass * the front controller for requests with file extensions and * return false. */ if (php_sapi_name () === 'cli-server' && isset ($_SERVER['REQUEST_URI']) && preg_match ('/\.[a-zA-Z0-9]+$/', parse_url ($_SERVER['REQUEST_URI'], PHP_URL_PATH))) { return false; } /** * Normalize slashes for servers that are still poorly * configured... */ if (get_magic_quotes_gpc ()) { function stripslashes_gpc (&$value) { $value = stripslashes ($value); } array_walk_recursive ($_GET, 'stripslashes_gpc'); array_walk_recursive ($_POST, 'stripslashes_gpc'); array_walk_recursive ($_COOKIE, 'stripslashes_gpc'); array_walk_recursive ($_REQUEST, 'stripslashes_gpc'); } /** * Check ELEFANT_ENV environment variable to determine which * configuration to load. Also include the Elefant version, * autoloader, and core functions, and set the default * timezone to avoid warnings in date functions. */ define ('ELEFANT_ENV', getenv ('ELEFANT_ENV') ? getenv ('ELEFANT_ENV') : 'config'); require ('conf/version.php'); require ('lib/Autoloader.php'); require ('lib/Functions.php'); date_default_timezone_set(conf ('General', 'timezone')); ini_set ('session.cookie_httponly', 1); ini_set ('session.use_only_cookies', 1); /** * Set the default error reporting level to All except Notices, * and turn off displaying errors. Error handling/debugging can * be done by setting conf[General][debug] to true, causing full * debug traces to be displayed with highlighted code in the * browser (*for development purposes only*), or by checking * the error log for errors. */ error_reporting (E_ALL & ~E_NOTICE); if (conf ('General', 'display_errors')) { ini_set ('display_errors', 'On'); } else { ini_set ('display_errors', 'Off'); } /** * Enable the debugger if conf[General][debug] is true. */ require ('lib/Debugger.php'); Debugger::start (conf ('General', 'debug')); /** * Include the core libraries used by the front controller * to dispatch and respond to requests. */ require ('lib/DB.php'); require ('lib/Page.php'); require ('lib/I18n.php'); require ('lib/Controller.php'); require ('lib/Template.php'); require ('lib/View.php'); /** * If we're on the command line, set the request to use * the first argument passed to the script. */ if (defined ('STDIN')) { $_SERVER['REQUEST_URI'] = '/' . $argv[1]; } /** * Initialize some core objects. These function as singletons * because only one instance of them per request is desired * (no duplicate execution for things like loading translation * files). */ $i18n = new I18n ('lang', conf ('I18n')); $page = new Page; $controller = new Controller (conf ('Hooks')); $tpl = new Template (conf ('General', 'charset'), $controller); $controller->page ($page); $controller->i18n ($i18n); $controller->template ($tpl); View::init ($tpl); /** * Check for a bootstrap.php file in the root of the site * and if found, use it for additional app-level configurations * (Dependency Injection, custom logging settings, etc.). */ if (file_exists ('bootstrap.php')) { require ('bootstrap.php'); } /** * Initialize the built-in cache support. Provides a * consistent cache API (based on Memcache) so we can always * include caching in our handlers and in the front controller. */ if (! isset ($cache) || ! is_object ($cache)) { $cache = Cache::init (conf ('Cache')); } $controller->cache ($cache); /** * Provide global access to core objects, although the preferred * way of accessing these is via the Controller object (`$this` * in handlers). */ $GLOBALS['i18n'] = $i18n; $GLOBALS['page'] = $page; $GLOBALS['controller'] = $controller; $GLOBALS['tpl'] = $tpl; $GLOBALS['cache'] = $cache; /** * Run any config level route overrides. */ if (file_exists ('conf/routes.php')) { $_routes = parse_ini_file ('conf/routes.php',true); if (isset($_routes['Disable'])){ foreach ($_routes['Disable'] as $_route => $_strict) { if ( (!$_strict && strpos($_SERVER['REQUEST_URI'],$_route) === 0 && $_SERVER['REQUEST_URI'] !== $_route) //match from left, exclude exact || ($_strict && $_SERVER['REQUEST_URI'] == $_route) // match exact ) { $page->body = $controller->run (conf ('General', 'error_handler'), array ( 'code' => 404, 'title' => 'Page not found.', 'message' => '' )); echo $page->render ($tpl, $controller); // render 404 page and exit return true; } }} if (isset($_routes['Redirect'])){ foreach ($_routes['Redirect'] as $_old => $_new) { if ($_old !== $_new && $_SERVER['REQUEST_URI'] == $_old) $controller->redirect($_new); }} if (isset($_routes['Alias'])){ foreach ($_routes['Alias'] as $_old => $_new) { if (strpos($_SERVER['REQUEST_URI'],$_old) === 0) { $_SERVER['REQUEST_URI'] = str_replace($_old,$_new,$_SERVER['REQUEST_URI']); break; } }} unset($_routes); } /** * Route the request to the appropriate handler and get * the handler's response. */ if ($i18n->url_includes_lang) { $handler = $controller->route ($i18n->new_request_uri); } else { $handler = $controller->route ($_SERVER['REQUEST_URI']); } $page->body = $controller->handle ($handler, false); /** * Control caching of the response */ if (conf ('Cache', 'control') && !conf ('General', 'debug')) { /* Cache control is ON */ if (session_id () === '' && $page->cache_control) { if (isset ($_SERVER["SERVER_SOFTWARE"]) && strpos ($_SERVER["SERVER_SOFTWARE"],"nginx") !== false) { /* Allow NGINX to cache this request - see http://wiki.nginx.org/X-accel */ $controller->header ('X-Accel-Buffering: yes'); $controller->header ('X-Accel-Expires: ' . conf ('Cache', 'expires')); } /* Standard http headers */ $controller->header ('Cache-Control: public, no-cache="set-cookie", must-revalidate, proxy-revalidate, max-age=0'); $controller->header ('Pragma: public'); $controller->header ('Expires: ' . gmdate ('D, d M Y H:i:s', time () + conf ('Cache', 'expires')) . ' GMT'); } else { if (isset ($_SERVER["SERVER_SOFTWARE"]) && strpos ($_SERVER["SERVER_SOFTWARE"],"nginx") !== false) { /* Do NOT allow NGINX to cache this request - see http://wiki.nginx.org/X-accel */ $controller->header ('X-Accel-Buffering: no'); $controller->header ('X-Accel-Expires: 0'); } /* Standard http headers */ $controller->header ('Pragma: no-cache'); $controller->header ('Cache-Control: no-cache, must-revalidate'); $controller->header ('Expires: 0'); } } else { if (isset ($_SERVER["SERVER_SOFTWARE"]) && strpos ($_SERVER["SERVER_SOFTWARE"],"nginx") !== false) { /* Do NOT allow NGINX to cache this request by default - see http://wiki.nginx.org/X-accel */ $controller->header ('X-Accel-Buffering: no'); $controller->header ('X-Accel-Expires: 0'); } } /** * Render and send the output to the client, using gzip * compression if conf[General][compress_output] is true. */ $out = $page->render ($tpl, $controller); if (extension_loaded ('zlib') && conf ('General', 'compress_output')) { ini_set ('zlib.output_compression', 4096); } @session_write_close (); echo $out; return true; }
function test_rest_api() { $c = new Controller(); $c->page(new Page()); $test_api = new RestTestApi(); // GET bap/bop/boop should match GET bap/bop/boop ob_start(); $_SERVER['REQUEST_METHOD'] = 'GET'; $c->params = array('bap', 'bop', 'boop'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->wrap('it works'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // POST foo/123 should match ALL foo/%d ob_start(); $_SERVER['REQUEST_METHOD'] = 'POST'; $c->params = array('foo', '123'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->wrap('123'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // GET bar/hello should match GET bar/%s ob_start(); $_SERVER['REQUEST_METHOD'] = 'GET'; $c->params = array('bar', 'hello'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->wrap('hello'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // POST bar/hello should fail to match GET bar/%s ob_start(); $_SERVER['REQUEST_METHOD'] = 'POST'; $c->params = array('bar', 'hello'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->error('Invalid action name'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // GET one/and/two/and should match GET one/%s/two/%s ob_start(); $_SERVER['REQUEST_METHOD'] = 'GET'; $c->params = array('one', 'and', 'two', 'and'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->wrap(array('and', 'and')); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // GET standard should match get_standard() ob_start(); $_SERVER['REQUEST_METHOD'] = 'GET'; $c->params = array('standard'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->wrap('it works'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // POST standard should fail to match get_standard() ob_start(); $_SERVER['REQUEST_METHOD'] = 'POST'; $c->params = array('standard'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->error('Invalid action name'); $exp = ob_get_clean(); $this->assertEquals($exp, $res); // GET asdf/foo should fail to match all ob_start(); $_SERVER['REQUEST_METHOD'] = 'GET'; $c->params = array('asdf', 'foo'); $c->restful($test_api); $res = ob_get_clean(); ob_start(); $test_api->error('Invalid action name'); $err = ob_get_clean(); $this->assertEquals($err, $res); }
/** * Run an internal request from one handler to another. */ public function run($uri, $data = array(), $internal = true) { $c = new Controller(count(self::$hooks) > 0 ? self::$hooks : conf('Hooks')); $c->page($this->_page); $c->i18n($this->_i18n); $c->template($this->_tpl); $c->cache($this->_cache); $handler = $c->route($uri); if (!isset(self::$called[$uri])) { self::$called[$uri] = 1; } else { self::$called[$uri]++; } return $c->handle($handler, $internal, $data); }