I am trying to build a custom MVC framework in PHP. I'm just starting out with this MVC and framework stuff and I'm not very good.
I created all the general stuff. The library for the model, controller and view and I got a general app up and running.
I would like to now incorporate some error handling. Primarily on the user side for bad urls. I want to make Page Not Found or 404 Errors. So i need a way to check for bad controllers,actions and query strings. And then send the users to a 404 page.
What is the best practice for doing this in an MVC environment?
EDIT
This is a learning based project it is not for production.
First of all it is not recommended to develop a custom framework your own if you are planning it for production. There are many great frameworks that you can make use of, with good flexibility and more importantly with more performance.
Coming to the problem. First write a custom exception handler for managing 404. I recommend to call them in your system/core class where you create Object of the controller and invoke the action, if they dont exists.
Add an error handler in your bootstrap.
For example, in mine I have:
public function error(){
$this->view->msg = 'Page not found';
$this->view->render('error/index');
}
Add a check for the method in the bootstrap. Where depends on your structure. If the method or controller isn't found call the error() method.
Thats probably the simplest way.
you can use .htaccess (if using apache) to route all request to your index page. Usually the the controller (and action), view is associated with a file or function. so, if you want to show custom error message, just check for the file, class, or its methods.
for example:- index.php?controller=control1&action=action1.
if(!file_exists('control1.php'))
if(!method_exists($controller, 'action1'))
//route to error handler
Well, if you are learning like you said, you shouldn't use your framework for production code because one thing is breaking the app at home and another one is loosing money with angry clients :)
Back to the problem. I've built my own custom framework for PHP applications and I have in my framework several Routers. The idea is that each Router handles a specific pattern of Route, like this:
http://someapplication.org/services/awebservice/
This code would call the ServiceRouter, which handles Routing web services. Before calling any Router I check if the Router exists, and if it does, then I would call a method to check if the route is valid, something like this:
if ( RoutingManager::RouterExists($route) )
{
$router = RoutingManager::GetRouter($route);
if ( !$router->IsValid )
RoutingManager::RedirectTo404();
}
The idea is to locate your 404 handler to a class, and delegate the task of routing into different classes to allow a variety of Routers and make your application more changeable (scalable)
Hope I can help!
Related
i want inside a controller and within the function init(){} to change the called action (requested action).
I mean if someone calls "www.mywebsite.com/myctrl/action1" i want inside the init function to call action2 instead, without redirecting the page and changing the url.
2- Is it possible to make a response inside init function, and stop calling the requested action ?
Best Regards
Wael
Why not just catch the URL in your config file and manually redirect it to the new action? It should work just fine and it would accomplish exactly what you want.
http://www.yiiframework.com/doc-2.0/guide-runtime-url-handling.html
I know this is not the way you wanted to do it but what is the advantage.
EDIT:
After you told me what you need to do then you really should not be doing this in this way. You should either be using AccessControl http://www.yiiframework.com/doc-2.0/yii-filters-accesscontrol.html or the beforeAction http://www.yiiframework.com/doc-2.0/yii-base-controller.html#beforeAction()-detail of the controller.
With the second option you can just throw an error and let Yii take care of the handling of it for you. The first one does kind of the same thing...
This problem is usually solved by using Apache's mod_rewrite or similar. There is no need to change your application at all. It's much more flexible than what yii2 has to offer and it'll be quicker because the web server is doing it natively.
The technique is generally used when migrating features, supporting different environments, sharing resources etc.
In the latest lecture at my University, my professor is returning the View template from the routes.
Route::get('/list', function() {
return View::make('list');
}
I don't understand how this is even possible. I thought ONLY the controller can interact with the View? How are routes interacting with the view? Does this mean any class in our app can call View::make and control what View template is rendered?
This raises the even larger question of what is the routes returning to? In other words, when our Laravel server gets a request, something in our server pings the routes and our routes returns a template. What is routes returning the template too? I'm presuming it is index.php, I know there are a lot of bootstrapping methods in there. Can someone explain the internal structure of how Laravel works and why we can render views from routes?
Laravel intentionally makes this possible for the sake of flexibility. That said, you'll want to let a controller return a view response in most cases.
There are times where this flexibility comes in handy; say you want to quickly setup a route to output some debug data. Building a controller action and view template would be overkill in this case when you can just output it directly from your route definition. There are other cases too, but the route -> controller -> view method handles most use cases.
Actually, when you make a request to your Laravel application the request goes through route matching and if the matching route found then the framework invokes the handler/action registered for that route. So, it's actually the handler and a handler could be anything which is callable. So, when you declare a route like this:
Route::get('url', 'action');
The framework just searches for the requested route and if that is available in the Route Collection then it invokes/calls/runs that action/handler and the handler returns the response (View). So, if it's somethiong like this;
Route::get('url', 'myHandler');
function myHandler()
{
//...
return View::make(...);
}
In this case your execution scope is the function and if it's a class method then the scope is different, but in both cases the handler is taking the control to return the response, that's all. So, while it's possible to return a View from a named function then it's also possible to return a View from an anonymous function as well. If you can access/use something (Some other class such as a service class for something) from your execution scope then it's possible to be used. The difference between a function and a class method is the scope. So it's possible and easy and also useful in some cases (for a single page or a small application) but not recommended way and neither it's good practice. Try to use a class instead of an anonymous function.
Why does Laravel allow us to render views in routes?
It's a framework and in other words, it's a toolbox with so many tools and this is (the question you asked) just another tool which increases it's (the framework's) usefulness. It's not the toolbox's job to make a better application but your's and the toolbox is just a helper which saves your effort and time to be more productive. So, you have shortcuts but you need to chose the right thing wisely.
Request lifecycle is quite well explained in Laravel documentation:
http://laravel.com/docs/4.2/lifecycle
Short answer: routes can return response (i.e. string, view), or can load controller which can return response as well. It's probably not very good practise to return view directly from routes, but can be useful in some cases.
Well im just starting a new project and i choosed symfony2 as the MVC framework for it. i want first to start this project by creating a BASE with a modular architecture, i mean an empty application that contains the main and common services like (navigation tree management , activation/desactivation of modules using database , logger..) or any other global functionalites that may come later after detailed conception. my modules will later be in bundles.
what im thinking of is a Core controller that would receive all requests, do all the treatement needed, init/change the services that the modules will use depending on configurations (files or DB) and cache (session/globals) then call the called controller and return the response. to do that i have to change the kernel to always dispatch toward this controller and give the action and the controller that the user called to it.
i did some project in symfony using only the standard edition and this is the first time trying to do internal customization so i dont have a lot of experience. if anyone think that my idea is bad and have other suggestions plz give them ill be extremly greatful !
Edit : i may specify that this BASE is just tests to find the perfect modular architecture so any other idea related to modularity in symfony would be a big help ! thanks
The kernel itself has nothing to do with the controller. That's all handled by the ControllerResolver. If you always want to handle each incomming request using the same controller, you can create your own ControllerResolver to Always return an instance of that controller. See http://symfony.com/doc/current/components/http_kernel/introduction.html#resolve-the-controller for more information about controller resolving.
However, I would recommend not to do it this way. Controllers should be very tin layers between User land and Logic land. All heavy things should be done in the logic layer, in services. And if you use services, you can better register them as listeners to the kernel.controller event than to call them from within a controller. See also http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html
I have an object oriented framework that uses a page design, where each page extends a view of the framework. So, for instance, my index page for a site is actually a class that implements the abstract class View provided by the framework. I hook the framework by including a startup script at the top of the page and after some processing the framework creates an instance of the page and processes its view data. To add flexibility to the system, I don't require the class name to be the name of the file itself. This allows me to create something like a support class and have it behave as the index for the /support/ subdomain.
I was initially passing the page's class name into the framework via the framework's constructor, but this added a few more steps to the top or bottom of the page. I currently obtain the class name via a pages table in the database identified by a filtered requested URI. I use this table to build navigation and adding an extra table column was practically free. This works OK as long as I have a database connection, but I'm trying to implement static page support for status messages and error reporting, and I need another method for creating an instance of the page's class. If I use the standard convention of naming the class the file's name, then I know I have a dependable way of extrapolating the class name. I don't really want to name all my classes index just for presentation reasons. What is some advice or some standards for how object oriented frameworks are initialized?
View.inc
<?php
abstract class View
{
abstract function getView();
}
?>
Startup.inc
<?php
require_once("View.inc");
require_once("CoreController.inc");
$Framework = new CoreController();
$Framework->Initialize();
exit;
?>
index.php
<?php
require_once("Startup.inc");
class Index extends View
{
public function getView()
{
echo "<pre>View Data</pre>";
}
}
?>
Within my framework I have a TemplateController that processes the page. The page class is known because it is mapped via another class. Without a database however, I would like to discover the class within, say, index.php without changing the way it's currently set up. Here is the actual code within the TemplateController.
//Get the View Class from Page
$view = $page->getPageClass();
//We need to catch View creation failure
$this->Page = new $view($this->Framework);
//Initialize the Page View
$this->Page->Initialize();
//Cache the Page View
$this->cacheView($this->Page, $page->getPageName(), $this->SiteID.TCS_PAGE_SORTID);
In the snippet above $view is the actual name of the class that was mapped from another controller. I'm passing a reference to the framework to the view's constructor and the rest is really irrelevant. I'm trying to come up with a similar mapping technique to identify the page class in the event the database is down. I like the one include line for the framework startup and would like to keep it that simple.
At the top of the TemplateController I have to also reinclude the actual page, since my startup script is at the top, the actual class is not included even though it is the requested page.
include($_SERVER['SCRIPT_FILENAME']);
Please review Stack Overflow question Identifying class names from $_SERVER['SCRIPT_FILENAME'] for more information on what I'm attempting to do.
Here is my checklist of page routing:
Adding new page must be quick
You must not be able to add page unintentionally
Application with million pages should not be slower or more bloated than application with 2 pages
Provide simple way and let people enhance it if they want
Allow easy re-factoring, such as moving several pages into different location
Make framework detect everything. Don't force user to specify base URL, etc.
Create simple to understand page names (hello/world).
Clean illegal characters from the URL
Make it possible to add static pages easy
Provide a way to generate URLs from page names.
Allow user to use pretty URLs by changing what appears before and after the page in the URL
And most importantly
- Stop copying, think about the best approach.
All of those suggestions I have used in the PHP UI framework - Agile Toolkit where I am a contributor.
Relevant sources: Agile Toolkit - a PHP Framework with jQuery, Agile Toolkit - PageManager.php, Agile Toolkit - URL.php and Agile Toolkit - PathFinder.php.
We wanted to make it really simple for developers. And secure too. And flexible. Therefore the "page" class is selected based on the URL. How? Quite easy:
/hello.html -> page_hello
/hello/world.html -> page_hello_world
Class then is mapped into filename. page/hello/world.php
Then there are cases when we want to use dynamic URLs too, such as: /article/234
Many frameworks implement a complex techniques here (arrays, regular expressions, front-controllers). We don't. There is mod_rewrite for this. Just rewrite this into page=article&id=234 and it works. And scales.
Apart from pages, there are other classes, but those classes can't be accessed directly. Therefore pages live under the /page/ folder, do distinguish them and maintain security.
Then comes the designer saying - "I won't write PHP". So we introduce static pages. If class page_hello_world is not defined, we'll look into template/skin/page/hello/world.html. That's a easy shortcut for designers and non-developers to add a new page.
What if a page is not found? Api->pageNotFound() is called. Feel free to redefine and load pages from SQL or display 404 page with a picture of a pink elephant.
And then there are some pages which come from add-ons. We don't want to enable them by default, but instead let users add them to their app somewhere. How?
class page_hello_world extends Page_MegaPage
Next question, is how we handle sub-pages of that page, since sometimes all the functionality can't fit on single page. Well, let's add support for page_edit() method (or any page_XX) as an alias for a sub-page inside those classes. Now add-on developer can include a multifunctional page which can be extended, customized and placed anywhere in the application.
Finally there are hackers, who want to do something really fast. For them we apply same technique to the API. You can define function page_hello_world in the API, and you don't need class.
Some might say that there are too many ways to do a simple thing. Oh well.
I hope that this was helpful.
Use __autoload(). Most major PHP frameworks use autoloading to streamline class loading.
You need some way to map a URL to an object, which is usually handled by a routing component in most frameworks. Might want to take a look at libraries such as https://github.com/chriso/klein.php, or the routing components of the major Frameworks out there (Zend, Symfony, etc.).
Why not to use information from URL to detect correct class? Since you won't use database for static pages, you have to somehow store mapping between URL and class in file. I.e. you will have a file mapping.php:
return array(
'about' => 'AboutView',
'copyright' => 'CopyrightView',
);
Here, about and copyright are URL components and values represent appropriate child classes of View. You can have URL's like http://example.com/index.php?page=about and use rewriting capabilities of webserver to make them look good.
You will change implementation of Page::getPageClass() in order to return correct view class. Depending on URL it will use static mappings from the file above or use database.
What's the problem with this approach?
Basically in order to have a profile Block u need - let's say profile controller, PhotoController, may be dashboard to show at the same page at the same time and to reach this in the zend framework is sometimes done through helper action() or forward, are ther better options?
I'm not sure I understood your question clearly. But you should avoid the action helper because it decreases significantly the performance of your application.
"The Action View helper basically creates an additional
dispatch, copying the request object,
and creating a loop-within-a-loop .
The setting up of the dispatch process
is a costly one, anyone who has
profiled their code will have seen
just how much of the process of a Zend
Framework application this eats up.
Creating a whole extra dispatch must
be a bad idea, even the Zend Framework
Performance Guide notes this fact."
This quote is from this article that explains why you should avoid it and what are the alternatives, why I believe to be the purpose of your question.
Maybe action helper or controller plugin using this method:
if ($condition) {
$this->getRequest()
->setModuleName('mymodule')
->setContollerName('mycontroller')
->setActionName('myaction');
// ->setDispatched(false); // redirect
}
Or controller plugin changing layout, using suitable view helpers.