I am currently learning zend expressive and I can see a couple of options as to how to access middleware within routes/actions.
In the expressive skeleton application, there is a HomePageFactory where the container is injected, then the router, template engine etc are pulled from the container and a new HomePageAction class is constructed using these and returned.
For example:
class HomePageFactory
{
public function __invoke(ContainerInterface $container)
{
$router = $container->get(RouterInterface::class);
$template = ($container->has(TemplateRendererInterface::class))
? $container->get(TemplateRendererInterface::class)
: null;
return new HomePageAction($router, $template);
}
I then needed a flash messenger and came across the following:
class SlimFlashMiddlewareFactory
{
public function __invoke($container)
{
return function ($request, $response, $next) {
// Start the session whenever we use this!
session_start();
return $next(
$request->withAttribute('flash', new Messages()),
$response
);
};
}
}
So this is slightly different and is adding the middleware to the request via an attribute. Which can then be retrieved where its needed by something like:
$flashMessenger = $request->getAttribute('flash');
So really my question is what are the advantages/disadvantages of these two methods of getting the FlashMessenger into the action?
If I have a UserService which deals with retrieving users from a database and therefore may be needed in multiple actions/routes, would I be better off modifying the HomePageAction & Factory (and any others that need it) to accept the UserService?
i.e.
class HomePageFactory
{
public function __invoke(ContainerInterface $container)
{
$router = $container->get(RouterInterface::class);
$template = ($container->has(TemplateRendererInterface::class))
? $container->get(TemplateRendererInterface::class)
: null;
$userService = $container->get(App\UserService::class);
return new HomePageAction($router, $template, $userService);
}
Or would I be better to go with how the FlashMessenger works (which seems a bit easier to manage) and add it to the request via an attribute to access it that way where needed?
i.e.
$userService = $request->getAttribute('UserService');
I'm wondering if there are any performance issues with the latter option, although I do understand that it could be done in this way only for specific routes rather than the UserServer being application wide.
My gut feeling (after writing out this question) is that really, this is a Service and not true middleware so I should really be Modifying the HomePageAction & Factory and adding the UserService that way rather than doing what seems easier and using the attribute ala FlashMessenger. But it would be very handy if a guru could help clarify this.
Many thanks in advance.
I don't know about performance differences since I haven't tested this. But I guess the question you need to ask yourself is what does the class do? Is it needed by other middleware before or after invoking the Action class or do you need it only in the Action class or somewhere else in the application.
In your case the UserService might take care of registering or updating an user. That stuff would be injected with the ActionFactory as your gut feeling tells you. However for authentication it would be different if that is done with middleware. First you need that class in your authentication middleware which you can pass on to your authorisation middleware with the request (or maybe only pass the authenticated user object, that's what I do).
So I guess the rule of thumb is that if something is needed to change the incoming Request or outgoing Response before / after the Action, you pass it on with the Request, otherwise inject it in the Action with a Factory.
If you start passing everything around with a Request, it will be very hard to keep track of. I find it much easier to inject as much as possible into the Action itself because then I can easily see what is needed inside that class and I can test it easier.
Things like a flash messenger, session and the authenticated user I would inject in the request.
Related
I could not find any information related two types of session access methods.
$request->session() from HTTP request instance and session() from session helper in Laravel 5.3 .
Is there any difference or which one to use when ?
How to send a get request to below controller method when using P.H.P unit
public function testMyMethod(Request $request){
$userExist = $request->session()->exists('user_id');
}
The Service Container is the core of the Laravel architecture. All services, components, and dependencies are registered there, and you can ask for any of them whenever you need it.
But Laravel provides more than one way to "ask for it". You have the global helper functions, you have facades, you have the "proper", "purer" injection of the component instance in the method signature. I think a big part of the philosophy of Laravel is a clean, easy, intuitive API. And in this case, it can be left up to your personal preference to define what "clean and easy" is, and whatever matches your style will be, by definition, intuitive to you.
I know there have been heated debates in the PHP community as to which method is "best", facades have been controversial, traditional dependency injection OOP purists say the only right way may be injecting the object with the controller method signature...
In the end, any of this various methods just grab the object from the same service container bag. It makes no difference performance-wise (I bet two function calls of indirection won't hit your performance) or otherwise. So, use whatever suits your style better. I personally do the "proper" injection anyway if I'm in a typical controller, but maybe I use the global helper if it makes for a cleaner, more readable code in its context.
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Session\Session;
use Facades\Illuminate\Contracts\Session\Session as SessionRealTimeFacade;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Request as RequestFacade;
use Illuminate\Support\Facades\Session as SessionFacade;
use PHPUnit\Framework\Assert;
class TestController extends Controller
{
public function sessionTest(Request $request, Session $session)
{
$options = [
$request->session()->get('_token'),
session()->get('_token'),
session('_token'),
$session->get('_token'),
SessionFacade::get('_token'),
SessionRealTimeFacade::get('_token'),
app('session')->get('_token'),
];
array_reduce(
$options,
function ($one, $other) {
Assert::assertEquals($one, $other);
return $other;
},
array_shift($options)
);
return 'All tests passed!';
}
}
And by the way, as you can see you have more than just 2 options :)
Of course, this doesn't apply to sessions only, the same goes for getting the request data, getting DB connection, and so much more.
(Also, I think you don't have the real-time facades in 5.3 yet)
Laravel Documentation
According to the Laravel Docs - https://laravel.com/docs/5.3/session - there is 'little practical difference between using the session via an HTTP request instance versus using the global session helper' and that both are testable.
->assertSessionHas($key, $value = null);
Laravel Up & Running - Matt Stauffer
In his book (page 320-), Matt talks through the different ways available to access session information:
Using the Session facade
session()->get('user_id');
Using the session() method on any given Illuminate Request object
Route::get('dashboard', function(Request $request) {
$request->session()->get('user_id');
});
Inject an instance of Illuminate\Session\Store
Route::get('dashboard', function(Illuminate\Session\Store $session) {
return $session->get('user_id');
});
Using the global session() helper
$value = session()->get('key);
$value = session('key);
His final comment is: If you're new to Laravel and not sure which to use, I'd recommend using the global helper.
So, I wouldn't worry about the differences and just go with what 'feels right' for you. If you have no preference either way, go with Matt Stauffer's recommendation.
Ultimately, the way that you normally use the two options, there is no practical difference between $request->session() and session(). However, they do not technically return the same objects.
session() will return the SessionManager. $request->session() will return the session Store.
The Store is actually the object that contains the session data.
The SessionManager is the object that manages the available session Stores. Most applications will only ever use one session store (the default one).
The reason why they seem to act as the same object is that when you call a method on the SessionManager that doesn't exist, it forwards that method call to the default session Store.
So, in your example, you use the exists() method. This method does not exist on the SessionManager. So, if you were to call session()->exists('user_id'), it would forward that call down to the default session Store, and it would be equivalent to calling $request->session()->exists('user_id').
One could argue that directly using $request->session() is more appropriate when attempting to access the session data, since that is what is returned, and doesn't require the additional forwarding function call. However, it is up to you to weigh the pros and cons to determine which method to use.
$request->session() and session() both are same thing.
There are two primary ways of working with session data in Laravel: the global function in session() helper and via a $request instance.
you can use it like this
public function testMyMethod(Request $request){
//$userExist = $request->session()->exists('user_id');
$userExist = $request->session()->has('user_id');
}
Actually native PHP session is a ready solution versus a better implementation from Laravel and Symfony that had put more thought into it. Read the explanation at Symfony Session configuration manual entry and you will understand i think. So there's a difference, but in the way it is thought and implemented. From the point of view from performance i think there isn't much. I will let you take your conclusions. Another entry that helps is at PHP manual
I have read a number of sources that hint that laravel facade's ultimately exist for convenience and that these classes should instead be injected to allow loose coupling. Even Taylor Otwell has a post explaining how to do this. It seems I am not the only one to wonder this.
use Redirect;
class Example class
{
public function example()
{
return Redirect::route("route.name");
}
}
would become
use Illuminate\Routing\Redirector as Redirect;
class Example class
{
protected $redirect;
public function __constructor(Redirect $redirect)
{
$this->redirect = $redirect
}
public function example()
{
return $this->redirect->route("route.name");
}
}
This is fine except that I am starting to find that some constructors and methods are beginning to take four+ parameters.
Since the Laravel IoC seems to only inject into class constructors and certain methods (controllers), even when I have fairly lean functions and classes, I am finding that constructors of the classes are becoming packed out with the needed classes that then get injected into the needed methods.
Now I am finding that if I continue down this approach that I will need my own IoC container, which feels like reinventing the wheel if I am using a framework like laravel?
For example I use services to control the business / view logic rather than controllers dealing with them - they simply route the views. So a controller will first take its corresponding service, then then the parameter in its url. One service function also needs to check the values from a form, so then I need Request and Validator. Just like that, I have four parameters.
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id)
{
// Call some method in the service to do complex validation
$validation = $my_service->doValidation($request, $validator);
// Also return the view information
$viewinfo = $my_service->getViewInfo($user_id);
if ($validation === 'ok') {
return view("some_view", ['view_info'=>$viewinfo]);
} else {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
This is a single example. In reality, many of my constructors already have multiple classes being injected (Models, Services, Parameters, Facades). I have started to 'offload' the constructor injection (when applicable) to method injection, and have the classes calling those methods use their constructors to inject dependencies instead.
I have been told that more than four parameters for a method or class constructor as a rule of thumb is bad practice / code smell. However I cannot see how you can really avoid this if you choose the path of injecting laravel facades.
Have I got this idea wrong? Are my classes / functions not lean enough? Am I missing the point of laravels container or do I really need to think of creating my own IoC container? Some others answers seems to hint at the laravel container being able to eliminate my issue?
That said, there doesn't seem to be a definitive consensus on the issue...
This is one of the benefits of constructor injection - it becomes obvious when you class is doing to much, because the constructor parameters grow too large.
1st thing to do is split up controllers that have too many responsibilities.
Say you have a page controller:
Class PageController
{
public function __construct(
Request $request,
ClientRepositoryInterface $clientrepo,
StaffRepositortInterface $staffRepo
)
{
$this->clientRepository = $clientRepo;
//etc etc
}
public function aboutAction()
{
$teamMembers = $this->staffRepository->getAll();
//render view
}
public function allClientsAction()
{
$clients = $this->clientRepository->getAll();
//render view
}
public function addClientAction(Request $request, Validator $validator)
{
$this->clientRepository->createFromArray($request->all() $validator);
//do stuff
}
}
This is a prime candidate for splitting into two controllers, ClientController and AboutController.
Once you have done that, if you still have too many* dependencies, its time to look for what i will call indirect dependancies (because i cant think of the proper name for them!) - dependencies that are not directly used by the dependant class, but instead passed on to another dependency.
An example of this is addClientAction - it requires a request and a validator, just to pass them to the clientRepostory.
We can re factor by creating a new class specifically for creating clients from requests, thus reducing our dependencies, and simplifying both the controller and the repository:
//think of a better name!
Class ClientCreator
{
public function __construct(Request $request, validator $validator){}
public function getClient(){}
public function isValid(){}
public function getErrors(){}
}
Our method now becomes:
public function addClientAction(ClientCreator $creator)
{
if($creator->isValid()){
$this->clientRepository->add($creator->getClient());
}else{
//handle errors
}
}
There is no hard and fast rule as to what number of dependencies are too many.
The good news is if you have built your app using loose-coupling, re-factoring is relatively simple.
I would much much rather see a constructor with 6 or 7 dependencies than a parameterless one and a bunch of static calls hidden throughout the methods
One issue with facades is that additional code has to be written to support them when doing automated unit testing.
As for solutions:
1. Resolving dependencies manually
One way of resolving dependencies, if you do not wish to do it via. constructors or methods injection, is to call app() directly:
/* #var $email_services App\Contracts\EmailServicesContract
$email_services = app('App\Contracts\EmailServicesContract');
2. Refactoring
Sometimes when I find myself passing too many services, or dependencies into a class, maybe I have violated the Single Responsibility Principe. In those cases, maybe a re-design is needed, by breaking the service or dependency into smaller classes. I would use another service to wrap up a related group of classes to serve something as a facade. In essence, it'll be a hierarchy of services/logic classes.
Example: I have a service that generate recommended products and send it out to users via email. I call the service WeeklyRecommendationServices, and it takes in 2 other services as dependency - a Recommendation services which is a black-box for generating the recommendations (and it has its own dependencies -- perhaps a repo for products, a helper or two), and an EmailService which maybe has Mailchimp as a dependency). Some lower-level dependencies, such as redirects, validators, etc. will be in those child services instead of the service that acts as the entry point.
3. Use Laravel global functions
Some of the Facades are available as function calls in Laravel 5. For instance, you can use redirect()->back() instead of Redirect::back(), as well as view('some_blade) instead of View::make('some_blade'). I believe it's the same for dispatch and some other commonly used facades.
(Edited to Add) 4. Using traits
As I was working on queued jobs today, I also observe that another way to inject dependencies is by using traits. For instance, the DispathcesJobs trait in Laravel has the following lines:
protected function dispatch($job)
{
return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
}
Any class that uses the traits will have access to the protected method, and access to the dependency. It's neater than having many dependencies in the constructor or method signatures, is clearer (about what dependencies are involved) than globals and easier to customize than manual DI container calls. The drawback is that each time you invoke the function you have to retrieve the dependency from the DI container,
Class methods that form a part of the routing mechanism in Laravel (middleware, controllers, etc.) also have their type-hints used to inject dependencies - they don't all need to be injected in the constructor. This may help to keep your constructor slim, even though I'm not familiar with any four parameter limit rule of thumb; PSR-2 allows for the method definition to be stretched over multiple lines presumably because it's not uncommon to require more than four parameters.
In your example you could inject the Request and Validator services in the constructor as a compromise, since they're often used by more than one method.
As for establishing a consensus - Laravel would have to be more opinionated for applications to be similar enough to utilise a one-size-fits-all approach. An easier call though is that I think facades will go the way of the dodo in a future version.
Not so much an answer but some food for thought after talking to my colleagues who have made some very valid points;
If the internal structure of laravel is changed between versions (which has happened in the past apparently), injecting the resolved facade class paths would break everything on an upgrade - while using the default facades and helper methods mostly (if not completely) avoids this issue.
Although decoupling code is generally a good thing, the overhead of injecting these resolved facade class paths makes classes cluttered - For developers taking over the project, more time is spent trying to follow the code which could be spent better on fixing bugs or testing. New developers have to remember which injected classes are a developers and which are laravels. Developers unfamiliar with laravel under the hood have to spend time looking up the API. Ultimately the likelihood of introducing bugs or missing key functionality increases.
Development is slowed and testability isn't really improved since facades are already testable. Rapid development is a strong-point of using laravel in the first place. Time is always a constraint.
Most of the other projects use laravel facades. Most people with experience using laravel use facades. Creating a project that doesn't follow the existing trends of previous projects slows things down in general. Future inexperienced (or lazy!) developers may ignore facade injection and the project may end up with a mixed format. (Even code reviewers are human)
Well your thoughts and concerns and correct and I had them as well.
There are some benefits of Facades ( I generally dont use them ), but if you do use just I would suggest using them only in the controllers, as the controllers are just entry and exit points for me at least.
For the example you gave I'll show how I generally handle it:
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
class ExampleController {
protected $request;
public function __constructor(Request $request) {
// Do this if all/most your methods need the Request
$this->request = $request;
}
public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id)
{
// I do my validation inside the service I use,
// the controller for me is just a funnel for sending the data
// and returning response
//now I call the service, that handle the "business"
//he makes validation and fails if data is not valid
//or continues to return the result
try {
$viewinfo = $my_service->getViewInfo($user_id);
return view("some_view", ['view_info'=>$viewinfo]);
} catch (ValidationException $ex) {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
}
class MyService implements MyServiceInterface {
protected $validator;
public function __constructor(Validator $validator) {
$this->validator = $validator;
}
public function getViewInfo($user_id, $data)
{
$this->validator->validate($data, $rules);
if ($this->validator->fails()) {
//this is not the exact syntax, but the idea is to throw an exception
//with the errors inside
throw new ValidationException($this->validator);
}
echo "doing stuff here with $data";
return "magic";
}
}
Just remember to break your code to small individual pieces that each one handles his own responsibility.
When you properly break your code, in most cases you will not have so many constructor parameters, and code will be easily testable and mocked.
Just one last note, if you are building a small application or even a page in a huge application for example a "contact page" and "contact page submit", you can surely do everything in the controller with facades, it simply depends on the complexity of the project.
I love the laravel due to its beautiful architecture.Now as from my approach i wouldnt inject all the facades in to the controller method only why? Injecting Redirect facades only in controller wrong practices as it might need in other. And mainly the things that are mostly used should be declared for all while for those who uses some or only then its best practice to inject them via method as when you declare at top it will hamper in your memory optimization as well as the speed of your code. Hope this would help
I'm trying to figure out if its bad practice to initiate a controller from within a router class. From what little I have been able to find about this, some say that the router shouldn't handle instantiating controllers. Below is how I started to develop my router class.
Example(note I'm omitting alot for the sake of typing.)
class Router {
private $url, $controller;
public function __construct($url)
{
$this->url = $url;
$this->map(); /* maps url to controller and action*/
/*dispatch controller*/
$this->dispatch();
}
private function dispatch()
{
$controller = new $this->controller();
$controller->executeAction();
}
}
To answer you question I'd so no it violates separation of concerns. The router shouldn't be worried what controller handles it's request, or rather how that controller came into being. It only needs to know that at some point, even in the event of a 404 that some controller will handle it.
Now injecting a controller into the route would be ok, then you could prototype it as an interface like so,
public function dispatch(ControllerInterface $Controller){
.....
}
otherwise you have to much hard linking, to much dependency, what if you need a second controller?
For example say you need an admin controller and a public controller, and a members controller. Do you then build 3 routers.
Personally the approach I am planning for a project I am working on is to use an event driven system, where there will be a group of controllers assigned by default as in a traditional routing system ( class/method/args... ) , say a controller folder will be searched for them. Other wise a controller will register itself to listen for a particular http request. So the flow is a bit like this.
Are there any registered listeners for this request,
if no are there any controllers in our controller folder that match the routing schema,
and at the very end is a 404 controller that will handle any request.
If any of these catch it the event ( that's being listened to is terminated ). The advantage of this over a purely hard wired route is, say I want to make a payment plugin, which needs a payment page, how do I put that in the controller folder as a third party vender? This way one only needs activate the plugin, which registers for the "payments" route, and listens within it's own package.
Maybe this is not a concern of you project but it's something to think about.
I have a mobile site that I added detection to for iPhones and other iOS devices. The iOS page needs a different layout and views than the regular pages (which are actually for older mobile devices). So, I have some code that does mobile detection, that part was easy. What I'd like to do is make it so that Zend automagically finds and uses the correct layout and view when an iOS device is detected, but that has turned out to be surprisingly hard...
I needed it to be up and running ASAP, so I did a quick and dirty hack that worked: in each action function, I have a simple If statement that detects if the iOS boolean flag has been set (which happens in the controller's init), and if so, overrides the layout and view explicitly. Existing code (in the actions):
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
So this works, but it's kinda ugly and hacky and has to be put in each action, and each action's Renderer has to be set, etc. I got to reading about the Zend ContextSwitch, and that seemed like exactly the kind of thing I should use (I'm still kind of new to Zend), so I started messing around with it, but can't quite figure it out.
In the controller's init, I'm initializing the ContextSwitch, adding a context for 'iphone' and setting the suffix to 'iphone', and now what I'd like to do is have a single place where it detects if the user is an iOS device and sets the context to 'iphone', and that should make it automatically use the correct layout and view. New code (in the controller's init):
$this->_helper->contextSwitch()->initContext();
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addContext('iphone', array('suffix' => 'iphone'));
$contextSwitch->setAutoDisableLayout(false);
if ($_SESSION['user']['iPhone']) {
//$this->_currentContext = 'iphone'; // Doesn't work.
//$contextSwitch->initContext('iphone'); // Doesn't work.
//$contextSwitch->setContext('iPhone'); // Not the function I'm looking for...
// What to put here, or am I barking up the wrong tree?
}
I did some reading on the contextSwitcher, and it seems like there is a lot of stuff on, e.g. setting it to be specific to each particular action (which I don't need; this needs to happen on every action in my app), and going through and modifying all the links to something like /osr/format/iphone to switch the context (which I also don't really need or want; it's already a mobile site, and I'd like the layout/view switch to be totally transparent to the user and handled only from the backend as it is with my quick and dirty hack). These seem like basically an equal amount of code to my quick and dirty hack. So... Anyone have some suggestions? I'm really hoping for just a single line like "$contextSwitch->setContext('iphone');" that I could use in an If statement in my controller's init, but the Zend documentation is awful, and I can't seem to find any examples of people doing something like this on Google or SO.
Ok I think I figured out how to put this into a plugin:
The Plugin:
//This is my own namespace for ZF 1.x library, use your own
class My_Controller_Plugin_Ios extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
parent::preDispatch($request);
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone');
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
register the plugin in your application.ini
resources.frontController.plugins.ios = "My_Controller_Plugin_Ios"
I think that's all there is to it. Although you may want to look into the userAgent plugin
ContextSwitch operates off the "format" property in the request object (by default). You need to set it somewhere in your app
$requestObject->setParam('format', 'iphone').
I'd set it in a bootstrap, or more appropriately, a controller plugin, but where it goes really depends on your app.
I don't use Zend ContextSwitch so I can't really help there, but you could use some inheritance in your controllers to set all layouts in just a couple of lines. Even though it might still be classed as a "hack" it is a way better hack
Now whenever you execute a action Zend first fires a number of other functions within the framework first, such as the routing, the preDispatch, Action helpers and so on. It also fires a number of things after the action such as PostDispatch. This can be used to your advantage.
First create a controller called something like "mainController" and let it extend Zend_Controller_action and in this controller create a function called predispatch()
Second. Extend your normal controllers to mainController. Since we now have a function called predispatch() Zend will automatically fire this on every controller, and if you do your iPhone/iOS check there it will automagically be performed on every action on every controller, as long as you don't overwrite the method in your controller (you can make this method final to prevent this). You can offcourse use a multitude of different non-Zend functions and/or helpers within the mainctroller to make the code as compact and reusable as possible Se example code below:
<?php
/**
*Maincontroller
*/
class MainController extends Zend_Controller_Action
{
/**
* Predispatch function is called everytime an action is called
*/
final public function preDispatch(){
//for security reasons, make sure that no one access mainController directly
$this->request = $this->getRequest();
if (strtolower($this->request->controller)=='main')
$this->_redirect('/index/index/');
//Check for iPhone
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
<?php
/**
*Othercontroller
*/
class OtherController extends MainController
{
/**
* The correct layout for IndexAction is already set by the inherited preDispatch
*/
public function indexAction(){
/* YOUR CODE HERE */
}
}
For a good overview of the dispatch process check these links (same picture in both):
http://nethands.de/download/zenddispatch_en.pdf
http://img.docstoccdn.com/thumb/orig/22437345.png
I don't know if my question should be asked here or not. Please let me know or move it/delete it if that is the case.
Question:
For the sake of learning, I'm making my own little MVC "library" for a very small personal website. Here is the process being used (please correct me if I'm not even close to doing it the right way):
All requests (except images, etc.) get sent through index.php (boostrap file).
The bootstrap file parses the request and extracts the controller and action, ex:
http://www.Domain.com/Controller/Action/Argument1/Argument2/...
Where Controller is the controller, action is the method called on the controller. In this case, it would end up being: Controller->Action ( Argument1, Argument2 );
Now, what if a user visits:
http://www.Domain.com/Controller/__destruct
or
http://www.Domain.com/Controller/__get/password
Current solution(s):
Run the request through a $config->getURIFilter () method or something
Do a check for method_exists () and is_callable (), etc.
Don't have any methods in the controller that aren't for handling a request
It just seems like this shouldn't be an issue to begin with and my design is wrong.
P.S. I've already looked at plenty of good MVC PHP frameworks (CodeIgniter, Yii, CakePHP, Zend, Swiftlet, etc.)
Either make your Controllers only handle specific actions, e.g.
/controllers
/user
loginController.php
logoutController.php
and then have classes that only do that one thing
class LoginController implements RequestHandler
{
public function handleRequest(Request $request, Response $response)
{
…
}
private function someAuxiliaryMethod() {
…
so that example.com/user/login will create a new LoginController and call the interface method handleRequest. This is a Strategy Pattern.
Or - if you dont want to split your controller actions like this - suffix your actions with a word (Zend Framework does this):
class UserController
{
public function loginAction() {
…
and in your bootstrap you add the suffix and invoke those methods then.
Yet another option would be to introduce a Router that can map URLs to Controller Actions and which can optionally do sanitizing to filter out malformed URLs, e.g. strip underscores.
I suggest that you treat any method preceded with an underscore as a private method (just show a not found page when you do your method_exists check if it starts with an underscore).
Just mark methods that you don't want to expose to your controller as private or protected.
Keep it simple!
I rolled my own MVC structure once for the same reason you're doing it, and the way I solved this was simply making an architectural decision to prefix all routable controller actions with the word "action", ie:
public function actionGetData(){
}
public function actionUpdateSomething() {
}
etc.
The reasons I did this:
You maintain full control over public/protected/private scope in your class without worrying about whether a method is inadvertently exposed via some URL.
It makes the routable actions obvious to the programmer. public function actionDoSomething() is clearly accessible via a public URL (/controllerName/doSomething), whereas public function getErDone() is not.
You're not forced to jump through hoops to identify routable actions. Simply prefix your incoming parameter with "action" and see if the method exists. Fast and simple.
I found this worked really well.