/** * Handle Request * Called on Each controller until request is handled * @param \Touchbase\Control\HTTPRequest &$request * @param \Touchbase\Control\HTTPResponse &$response * @return mixed $result */ public function handleRequest(HTTPRequest &$request, HTTPResponse &$response) { $handlerClass = "{$this}"; while ($handlerClass != __CLASS__) { $getClassUrlHandler = get_class_vars($handlerClass); $urlHandlers = $getClassUrlHandler['urlHandlers']; if (!empty($urlHandlers)) { foreach ($urlHandlers as $rule => $action) { if (is_numeric($rule)) { $rule = $action; $action = $this->defaultAction; } //\pre_r("Testing '$rule' with '".$request->remaining()."' on '$this'"); if ($params = $request->match($rule)) { //\pre_r("Rule '$rule' matched to action '$action' on '$this'"); if ($action[0] == "\$") { $action = $params[substr($action, 1)]; } if (!$action) { user_error("Action not set; using default action method name 'index'", E_USER_NOTICE); $action = 'index'; } else { if (!is_string($action)) { user_error("Non-string method name: " . var_export($action, true), E_USER_ERROR); } } if (!$this->hasMethod($action) || !$this->isEnabled() || !$this->application()->isEnabled()) { return $this->throwHTTPError(404, "Action '{$action}' isn't available on class {$this}"); } if (!$this->isAllowed() || !$this->application()->isAllowed() || !$this->checkAccessAction($action)) { return $this->throwHTTPError(403, "Action '{$action}' isn't allowed on class {$this}"); } if (!$request->allParsed()) { return $this->throwHTTPError(404, "I can't handle sub-URLs of a {$this} object"); } //Set Controller Vars and Call Action $this->setParams($request->urlParams()); //Call Controller Init $this->init(); if (!$this->baseInitCalled) { user_error("init() method on class '{$this}' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING); } return $this->{$action}($request); } } } //Update $handlerClass $handlerClass = get_parent_class($handlerClass); } //Nothing matches. return $this; }
/** * Handle Request * @param HTTPRequest &$request * @param HTTPResponse &$response * @return string */ public function handleRequest(HTTPRequest &$request, HTTPResponse &$response) { //Pass through to Controller try { $response = new WebpageResponse($this); if ($request->isPost()) { $validation = Validation::create(); $this->validatePostRequest($request, $response, $validation); } $body = parent::handleRequest($request, $response); } catch (\Exception $e) { if (!$e instanceof HTTPResponseException) { error_log(print_r($e, true)); $e = new HTTPResponseException($e->getMessage(), 500); $e->response()->setHeader('Content-Type', 'text/plain'); } $body = $this->handleException($e); //If handleException returns a redirect. Follow it. if ($body instanceof \Touchbase\Control\HTTPResponse) { //TODO: Should we always return the response? if ($body->hasFinished()) { return $response = $body; } } $response->setBody($body); $response->setStatusCode($e->getCode(), $e->getMessage()); } if ($this->request()->isMainRequest()) { $applicationTitles = []; $application = $this->application; while ($application != null) { if (isset($application::$name)) { $applicationTitles[] = $application::$name; } $application = $application->application; } $this->response()->assets()->pushTitle(array_reverse($applicationTitles)); if (isset(static::$name)) { $this->response()->assets()->pushTitle(static::$name); } } return $body; }
/** * Handle Request * This method will attempt to load a real resource located on the server if one exists. * @param \Touchbase\Control\HTTPRequest $request * @param \Touchbase\Control\HTTPResponse &$response * @return mixed */ private static function handleRequest(HTTPRequest $request, HTTPResponse &$response) { //TopSite Preview if (isset($_SERVER["HTTP_X_PURPOSE"]) && $_SERVER["HTTP_X_PURPOSE"] == "preview") { $assetFile = File::create([BASE_PATH, 'public_html', 'preview.html']); //Favicon } else { if ($request->urlSegment() == "favicon") { $assetFile = File::create([BASE_PATH, 'public_html', 'favicon.ico']); if (!$assetFile->exists()) { //Write an empty favicon if one doesn't exist $assetFile->write(base64_decode("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAFklEQVR42mNkoBAwjhowasCoAcPFAAAmmAARm5JBWgAAAABJRU5ErkJggg==")); } //Asset Map } else { $assetFilePath = static::pathForAssetMap($request->urlSegment()); if ($assetFilePath) { $assetFile = File::create([BASE_PATH, $assetFilePath, implode("/", $request->urlSegments(1)) . '.' . $request->extension()]); } } } if (isset($assetFile)) { $supportedAssets = ["css" => "text/css; charset=utf-8", "js" => "text/javascript; charset=utf-8", "htc" => "text/x-component", "png" => "image/png", "jpg" => "image/jpg", "gif" => "image/gif", "svg" => "image/svg+xml", "ico" => "image/x-icon", "otf" => "application/font-otf", "eot" => "application/vnd.ms-fontobject", "ttf" => "application/font-ttf", "woff" => "application/font-woff", "woff2" => "application/font-woff2", "apk" => "application/vnd.android.package-archive", "ipa" => "application/octet-stream"]; if ($assetFile->exists() && array_key_exists($assetFile->ext(), $supportedAssets)) { session_cache_limiter(false); $response->addHeader("Content-Type", $supportedAssets[$assetFile->ext()]); $response->addHeader("Content-Disposition", "attachment; filename=" . $assetFile->name); $response->addHeader('Content-Length', $assetFile->size()); //TODO: Should be done in response setBody! if (php_sapi_name() != 'cli-server' && static::config("assets")->get("x_sendfile", false)) { $response->addHeader("X-Sendfile", $assetFile->path); } else { $response->setBody($assetFile->read()); } return true; } } return false; }
/** * Validate Post Request * This method adds default validation to input fields based on the served HTML. * If any errors are found, the redirect will be made on the response object with the errors. * @param \Touchbase\Control\HTTPRequest $request * @param \Touchbase\Control\HTTPResponse &$response * @param \Touchbase\Utils\Validation &$validation * @return VOID */ protected function validatePostRequest($request, &$response, &$validation) { if (!$request->isMainRequest()) { return; } $formNameToken = $request->_VAR("tb_form_token"); $formName = substr($formNameToken, 0, strrpos($formNameToken, "_")); if (isset($formNameToken) && ($form = SessionStore::get($formNameToken, false))) { SessionStore::consume($formName, $formNameToken); $data = $request->_VARS(); libxml_use_internal_errors(true); $dom = new \DOMDocument(); $dom->loadHtml(gzinflate(base64_decode($form)), LIBXML_NOWARNING | LIBXML_NOERROR); if ($dom->documentElement->getAttribute("novalidate")) { return; } $formValidation = Validation::create($formName); $privateFields = []; foreach (["input", "textarea", "select"] as $tag) { foreach ($dom->getElementsByTagName($tag) as $input) { if ($input->hasAttributes() && ($inputName = $input->getAttribute("name"))) { $inputType = $input->getAttribute("type"); $inputValidation = Validation::create($inputName); if ($inputType === "password") { $privateFields[] = $inputName; } $inputValidation->type($inputType); if ($inputType === "file" && $input->hasAttribute("accept")) { $validTypes = explode(",", $input->getAttribute("accept")); //File Extentions $fileExt = array_filter($validTypes, function ($value) { return strpos($value, ".") === 0; }); if (!empty($fileExt)) { $inputValidation->addRule(function ($value) use($fileExt) { $name = $value['name']; if (is_array($name)) { foreach ($name as $nme) { if (!in_array(pathinfo($nme, PATHINFO_EXTENSION), $fileExt)) { return false; } } return true; } return in_array(pathinfo($name, PATHINFO_EXTENSION), $fileExt); }, "A file uploaded did not have the correct extension"); } //Mime Types $validTypes = array_diff($validTypes, $fileExt); $implicitFileMime = array_filter($validTypes, function ($value) { return strpos($value, "/*") !== false; }); if (!empty($implicitFileMime)) { $inputValidation->addRule(function ($value) use($implicitFileMime) { $tmpName = $value['tmp_name']; if (is_array($tmpName)) { foreach ($tmpName as $tmp) { $mime = strstr(File::create($tmp)->mime(), "/", true) . "/*"; if (!in_array($mime, $implicitFileMime)) { return false; } } return true; } $mime = strstr(File::create($tmpName)->mime(), "/", true) . "/*"; return in_array($mime, $implicitFileMime); }, "A file uploaded did not have the correct mime type"); } $validTypes = array_diff($validTypes, $implicitFileMime); $fileMime = array_filter($validTypes, function ($value) { return strpos($value, "/") !== false; }); if (!empty($fileMime)) { $inputValidation->addRule(function ($value) use($fileMime) { $tmpName = $value['tmp_name']; if (is_array($tmpName)) { foreach ($tmpName as $tmp) { if (!in_array(File::create($tmp)->mime(), $fileMime)) { return false; } } return true; } return in_array(File::create($tmpName)->mime(), $fileMime); }, "A file uploaded did not have the correct mime type"); } } if ($input->hasAttribute("required")) { $errorMessage = null; if ($placeholder = $input->getAttribute("placeholder")) { $errorMessage = sprintf("Please complete the `%s` field", $placeholder); } $inputValidation->required($errorMessage); } if ($input->hasAttribute("readonly")) { $inputValidation->readonly($input->getAttribute("value")); } if ($input->hasAttribute("disabled")) { $inputValidation->disabled(); } if ($minLength = $input->getAttribute("minlength")) { $inputValidation->minLength($minLength); } if ($maxLength = $input->getAttribute("maxlength")) { $inputValidation->maxLength($maxLength); } if (in_array($inputType, ["number", "range", "date", "datetime", "datetime-local", "month", "time", "week"])) { if ($min = $input->getAttribute("min")) { $inputValidation->min($min, $inputType); } if ($max = $input->getAttribute("max")) { $inputValidation->max($max, $inputType); } } if ($pattern = $input->getAttribute("pattern")) { $inputValidation->pattern($pattern); } if (count($inputValidation)) { $validation->addRule($formValidation->addRule($inputValidation)); } } } } if (!$validation->validate($data)) { $response->withData(array_diff_key($data, array_flip($privateFields))); $response->redirect(-1)->withErrors($validation->errors, $formName); } } else { $response->redirect(-1)->withErrors(["Session timed out, please try again."], $formName); } }