So my issue is, I have no idea how to handle page not found handling, Since routing runs every time a route is added if you do anything other than the first route it'll have 2 outputs.
Routes.php
Route::set('index.php', function() {
Index::CreateView('Index');
});
Route::set('test', function() {
Test::CreateView('Test');
});
?>
Routes.php (class)
<?php
class Route {
public static $validRoutes = array();
public static function set($route, $function) {
self::$validRoutes[] = $route;
$url = $_GET['url'];
if($url == $route) {
$function->__invoke();
die();
}
if(!in_array($url, self::$validRoutes)) {
Controller::CreateView("404");
die();
}
}
}
?>
I'm trying to understand how I'd even handle if its not found.
How about something like:
<?php
class Router
{
public $routes = [];
public function add($route, $function)
{
$this->routes[$route] = $function;
}
public function route($path)
{
$function =
$this->routes[$path] ??
$this->routes['404'] ?? null;
if(is_null($function))
http_response_code(404) && exit('404 Not Found.');
$function->__invoke();
}
}
$router = new Router;
$router->add('foo', function() {
echo 'bar';
});
$router->add('greeting', function() {
echo 'hello earth';
});
$router->route('greeting');
Output:
hello earth
Rather than trying to resolve your routes upon each addition, you can add them all, and then resolve/dispatch/route later.
I've simplified the Router::routes array to use the paths as keys.
When resolving, if the path cannot be found (the index does not exist) it will try and check for a '404' key in the routes array. Failing that it responds with a basic 404.
Related
Heres the code for the route:
<?php
use app\controllers\Home;
use app\core\App;
$app = new App();
$app->router->get('/', function(){
echo "hello there";
});
$app->router->get('user/', 'user');
$app->router->get('/contact', [Home::class,'contacts']);
$app->router->post('/contact', function(){
echo "handlecontact";
});
$app->run();
Heres the code for the router core class:
<?php
namespace app\core;
class router{
public Request $request;
public Response $response;
protected array $routes = [];
public function __construct(Request $request,Response $response)
{
$this->request = $request;
$this->response = $response;
}
public function get($path, $callback){
$this->routes['get'][$path] = $callback;
}
public function post($path, $callback){
$this->routes['post'][$path] = $callback;
}
public function resolve(){
$path = $this->request->getpath();
$method = $this->request->getmethod();
$callback = $this->routes[$method][$path] ?? false;
if($callback == false){
$this->response->setstatuscode(404);
return "not found";
}
if(is_array($callback)){
$callback[0] = new $callback[0]();
}
return call_user_func($callback,$this->request);
}
I want to be able to declare a route and pass a parameter to it. If i do it so like that it tells me route not found.
Like i want to be able to write the route like this:
$app->router->get('/contact/{id}', function(){
echo $id; // to echo it out like this from the controller
});
Can someone be of good help or also add something to the idea. Thanks in advance
Under htdocs folder i have created folder called PHP under that i have created file called index.php. I have written wrouting PHP code but it is not working.
index.php
<?php
echo "Welcome" . "<br>";
$routes = [];
route('/', function () {
echo "Home Page";
});
route('/login', function () {
echo "Login Page";
});
route('/about-us', function () {
echo "About Us";
});
route('/404', function () {
echo "Page not found";
});
function route(string $path, callable $callback) {
global $routes;
$routes[$path] = $callback;
}
run();
function run() {
global $routes;
$uri = $_SERVER['REQUEST_URI'];
$found = false;
foreach ($routes as $path => $callback) {
if ($path !== $uri) continue;
$found = true;
$callback();
}
if (!$found) {
$notFoundCallback = $routes['/404'];
$notFoundCallback();
}
}
http://localhost/PHP/
Welcome
Page not found
http://localhost/PHP/index.php/login/ (getting below output)
Welcome
Page not found
Expected output
Welcome
Login Page
Here wt am doing mistake, can anyone explain and update my code
According to Lavarel documentation, if I use subdomain routing for multiple subdomains, I have to pass the subdomain as the first argument of callbacks functions and controllers methods:
Route::domain('{sub}.example.org')->group(function () {
Route::get('a', function ($sub) { /* ... */ });
Route::get('b', function ($sub) { /* ... */ });
Route::get('c/{c}', function ($sub, $c) { /* ... */ });
Route::get('d/{d}', function ($sub, $d) { /* ... */ });
});
In other words, I have to carry the $sub variable everywhere. Assuming I don't care about its value, can I avoid this and just do something like this(this does not work for multiple argument):
Route::domain('{sub}.example.org')->group(function () {
Route::get('a', function () { /* ... */ });
Route::get('b', function () { /* ... */ });
Route::get('c/{c}', function ($c) { /* ... */ });
Route::get('d/{d}', function ($d) { /* ... */ });
});
If I do this, $c and $d will be the value of the subdomain.
Assuming I don't care about the subdomain value and I have many routes, is there a way to ignore it?
We had a similar thing with a dynamical prefix. Our solution was:
First we unset the parameter if it existed in the "App/Http/Controllers/Controller.php", which is the class your controllers most likely extend.
public function callAction($method, $parameters)
{
if (isset($parameters['clientident'])) {
unset($parameters['clientident']);
}
return parent::callAction($method, $parameters);
}
This will unset the parameter for every function call in every controller and should do away with your problem.
But because we needed it in our route() function, we created a "App/Http/helpers.php" file for
function route($name, $parameters = [], $absolute = true)
{
if (!isset($parameters['clientident'])) {
// If the given value is not an array, wrap it in one.
$parameters = Arr::wrap($parameters);
if (Auth::check()) {
$temp = ['clientident' => Auth::user()->getClientIdent()];
}
else if (request()->clientident) {
$temp = ['clientident' => request()->clientident];
}
else {
$temp = ['clientident' => 'general'];
}
$parameters = array_merge($parameters, $temp);
}
return app('url')->route($name, $parameters, $absolute);
}
and added the new helpers file in the "bootstrap/autoload.php"
require __DIR__.'/../app/Http/helpers.php';
require __DIR__.'/../vendor/autoload.php'; << this line was already here
This works because Taylor wrote an check in front of every helper function if it is already set, so it's pretty easy to override them
if (! function_exists('route')) {
function route($name, $parameters = [], $absolute = true)
{
return app('url')->route($name, $parameters, $absolute);
}
}
is the code in the helpers.php in the Illuminate/Foundation directory.
I guess the route function has to be handled a little bit different in your case. You should be able to get the subdomain with domain helper functions and add it there as parameter back again, which is most likely much easier than our if elseif else case above.
I'm trying to make a very basic routing class and learn PHP closures by example. Basically, I want to make a routing feature like in Laravel but only with using closures.
function get($uri)
{
if($uri == '/account')
{
return true;
}
else
{
return false;
}
}
function Boo()
{
echo "Boo";
}
$route = new Route();
$route->get('/account', function() {
return $route->Boo();
});
I can do this without closures and see "Boo" as output.
How can I do this with closures? I currently see a blank output.
Ps. Functions are in correct class.
You need to actually accept the closure as a parameter of your get method, and call it, here's an example
class Route
{
function get($uri, Closure $closure=null)
{
if($uri == '/account')
{
// if the closure exists, call it, passing it this instance as its parameter
if (null !== $closure) {
$closure($this);
}
return true;
}
else
{
return false;
}
}
function Boo()
{
echo "Boo";
}
}
$route = new Route();
// have the closure accept a route as it's parameter
$route->get('/account', function($route) {
return $route->Boo();
});
I have this problem with Slim framework. I have class Template that have render method and I want for Slim to render objects of this class if they are returned by route handlers
$app->get('/test', function() {
return new Template('main', function() use ($error) {
return array(
'content' => "Hello"
);
});
});
it work I created child class (in System.php)
class System extends Slim {
function __constructor() {
Slim::__construct();
}
private function auto_render_fun($callable) {
$app = $this;
return function() use ($callable, $app) {
$args = func_get_args();
$ret = call_user_func_array($callable, $args);
if ($ret instanceof Template) {
//render Template - Slim ignore return value
$app->response()->body($ret->render());
}
};
}
protected function mapRoute($args) {
$last = count($args)-1;
$args[$last] = $this->auto_render_fun($args[$last]);
return Slim::mapRoute($args);
}
}
I wanted to do the same thing with notFound
$app->notFound(function () use ($app) {
$response = $app->response();
$response['Content-Type'] = 'text/html';
return new Template('main', function() {
return array('content' => new Template('error_404', null));
});
});
so I've overwritten notFound function to wrap Closure and render it's return value
First I try to use smaller code
public function notFound($callable = null) {
if (!is_null(($callable))) {
$this->router->notFound($this->auto_render_fun($callable));
} else {
Slim::notFound();
}
}
I also try this (copy and modify old code).
public function notFound($callable = null) {
if ( !is_null($callable) ) {
$this->router->notFound($this->auto_render_fun($callable));
// $this->router->notFound($callable); // old line
} else {
ob_start();
$customNotFoundHandler = $this->router->notFound();
if ( is_callable($customNotFoundHandler) ) {
call_user_func($customNotFoundHandler);
} else {
call_user_func(array($this, 'defaultNotFound'));
}
$this->halt(404, ob_get_clean());
}
}
but the reason why it didn't work is that it trow Slim_Exception_Stop that's suppose to be cached by Slim here is that line for code that call $this->notFound();
https://github.com/codeguy/Slim/blob/master/Slim/Slim.php#L1160
it's inside try..catch.
Here is stack trace (I cached it inside notFound function - but it should be handled in Slim class).
Slim_Exception_Stop in file libs/Slim/Slim/Slim.php at 862
0: Slim->stop() in libs/Slim/Slim/Slim.php at 882
1: Slim->halt(integer, string) in libs/System.php at 187
2: System->notFound() in libs/Slim/Slim/Slim.php at 1161
3: Slim->call() in libs/Slim/Slim/Middleware/Flash.php at 84
4: Slim_Middleware_Flash->call() in libs/Slim/Slim/Middleware/MethodOverride.php at 91
5: Slim_Middleware_MethodOverride->call() in libs/Slim/Slim/Middleware/PrettyExceptions.php at 65
6: Slim_Middleware_PrettyExceptions->call() in libs/Slim/Slim/Middleware/PrettyExceptions.php at 65
7: Slim_Middleware_PrettyExceptions->call() in libs/Slim/Slim/Slim.php at 1098
8: Slim->run() in index.php at 573
Ok, I found the reason, exception was handled as it should but there was not content. I wrongly assume that it's something with Classes, but simply the Slim code have bugs.
public function halt( $status, $message = '' ) {
$this->cleanBuffer();
$this->response->status($status);
$this->response->body($message);
$this->stop();
}
function overwrite data that you use in the handler like this
$app->notFound(function() {
$app->response()->body('Error');
});
won't work, function not found should look like this
public function notFound($callable = null) {
if (!is_null(($callable))) {
$this->router->notFound($this->auto_render_fun($callable));
} else {
$customNotFoundHandler = $this->router->notFound();
if (is_callable($customNotFoundHandler)) {
call_user_func($customNotFoundHandler);
} else {
call_user_func(array($this, 'defaultNotFound'));
}
$this->response->status(404);
$this->stop();
}
}