Where to put new objects generated by middleware? - php

PSR-7 is going to be standardized soon (I believe). That's got me thinking about middlewares, such as used by Phly, StackPHP, and ConnectJS.
The way ConnectJS works is that it modifies the request object when a middleware needs to add something. For example, cookie-session creates a session property on the req object:
app.use(session({
keys: ['key1', 'key2']
}))
app.use(function (req, res, next) {
var n = req.session.views || 0 // <-- req.session is managed by the session middleware
req.session.views = ++n
res.end(n + ' views')
})
With PSR-7, both our Request and Response objects are (supposed to be) immutable, so how are we supposed to pass along additional data like this? i.e. where would be the best place to store a 'session' object or a 'user' object created by an authentication middleware?

Request and Response objects in PSR-7 are implemented as value objects, hence they are immutable.
Every time you need a different object, you create a new instance from the previous one, like
$newRequest = $oldRequest->withMethod('GET');
and from that point on use the new instance.
In middlewares you would have to pass the new instance to the next() function that calls the next middleware (see here for example).
If you need to store in the request object additional data computed from your current request, in the ServerRequestInterface there are defined the withAttribute and the withAttributes methods that allow you to do exactly that.
A common use case for this is for storing the results of routing, but you can surely use them to store other additional data of the request, like session or user data

Do not store at all. Inject it as parameter into consumer function. For instance:
function doSomething(reqest, response, session, user, foo, bar, ...)
Be explicit.

Related

Get Session data in store function

Is it right to get Session data in a store function and store them into db?
public function store(){
...
$idgroup = Session::get('invitation_userid')];
...
}
Or need a store function always a Request Object?
public function store(Request $request){
...
$idgroup = $request('idgroup');
...
}
In both functions is of course a validation part for the input data.
Both approaches are fine, but you should use them appropriately to your use case, I prefer to use the Request data. The main difference is that if u store that inside the Session it will be available application wide, while if u send inside Request it will be available inside the method only
This depends entirely on the context of what your controller is actually named, how this data is being used and why you are not using a database session driver in the first place if you want to do this.
You could simply use the database driver for the session:
https://laravel.com/docs/5.7/session#introduction
It also depends on what your controller is named if you strictly want to follow restful routes:
https://gist.github.com/alexpchin/09939db6f81d654af06b
To answer the second question you don't always need a Request object in your store action. Most of the time you won't even see a Request object because you are simply creating an entirely new resource.
The Global Session Helper
You may also use the global session PHP function to retrieve and store data in the session. When the session helper is called with a single, string argument, it will return the value of that session key. When the helper is called with an array of key / value pairs, those values will be stored in the session:
$value = session('key');

Laravel 5: how to reset builtin throttle / ratelimiter?

Im using Laravel's builtin throttle like this:
//File: Kernal
protected $middlewareGroups = [
'api' => ['throttle:10,3']
];
However, I would like to reset the count after certain action in one of my controllers (for example after successful login).
I can see this middleware uses RateLimiter and that has a public method called clear.
The problem is, how to use this? Because it depends upon the key from ThrottleRequests middleware.
To get the object of ThrottleRequests I need instance of RateLimiter
To get the object of RateLimiter, I need instance of Cache.
.
.
all in all, there is no end to how to use it..
Any idea?
Thanks
As your question is tagged with Laravel v5.5, here's what applies there:
For Login attempts specifically:
You can use the Illuminate\Foundation\Auth\AuthenticatesUsers trait in your controller, so you would have access to the clearLoginAttempts method, which calls the clear() method on the RateLimiter instance with the correct key without the need to provide the key.
Actually if you look at how Illuminate\Foundation\Auth\ThrottlesLogins::clearLoginAttempts() is implemented, you can see that the right key can be retrieved by $this->throttleKey($request), once your controller uses the AuthenticatesUsers trait.
In general:
You can always get the Illuminate\Cache\RateLimiter instance by using app(\Illuminate\Cache\RateLimiter::class), which would in turn contain all the configured limiters and the cache as well. The problem is that it is not possible to get the cache keys from this point of view. So you really have to find out where and how the key was set in the first place, so you can use the same key for the reset.
The standard ThrottleRequests middleware sets the key in the handle() method, but the actual key would depend on where and how your throttling is configured (e.g.: is it a named limiter or only set using the numeric parameters, was ->by(...) called on it to set the key explicitly etc.)
If you only need to find the key for one particular limiter, probably you can set a breakpoint in the handle() method and just check.
Your case
In your particular case, as it is not a named limiter, the handle() method will call resolveRequestSignature to get the key. I don't think you can easily access the Middleware instance from a Controller. What you can do is to check how that method generates the key and basically copy that piece of code to replicate the same keys, but I'd not recommend that as it is a dirty and fragile solution.
Ifyou check, you'll see the key can be reproduced as something like:
if ($user = $request->user()) {
$key = sha1($user->getAuthIdentifier());
}
elseif ($route = $request->route()) {
$key = sha1($route->getDomain().'|'.$request->ip());
}
But in more recent Laravel versions you can explicitly set the key which is much cleaner and reliable solution:
In Laravel 8
Now as the question is fairly old, most people would rather use the latest version of Laravel (v8 as of 2021/02/12), so for them the documentation includes the way to "segment" the limiters aka. become able to apply separate limit counters for different requests based on the request (or session data, etc.). In fact the by() method actually sets the key of the limiter. So you can set up one or more named limiters like:
RateLimiter::for('my_per_ip_limiter', function (Request $request) {
return Limit::perMinute(100)->by($request->ip());
});
This means the limiter named my_per_ip_limiter will use the IP as the key, so any time in your controllers you could call:
app(\Illuminate\Cache\RateLimiter::class)->clear($request->ip());
to reset the limiter for a particular IP. Or to get the number of attempts so far:
$attempts_so_far = app(\Illuminate\Cache\RateLimiter::class)->attempts($request->ip());
Indeed instead of IP you could use any variable of the request (or session or whatever).
However there is no way (I think) to differentiate between the named limiters. So if the same key is used for another limiter as well, their hits will be counted together* and clear together. So giving a name like my_per_ip_limiter to the limiter is only useful so you can assign that limiter to particular routes by the name, e.g.:
Route::post( 'login', 'Auth\LoginController#login' )
->middleware('throttle:my_per_ip_limiter');
But if you really need named limiters to reset individually, you have to use a unique key, for example prefixing it with something, e.g.:
RateLimiter::for('my_other_ip_limiter', function (Request $request) {
return Limit::perMinute(100)->by('other_'.$request->ip());
});
This can be cleared independently from the other one:
// reset my_other_ip_limiter, but not my_per_ip_limiter :
app(\Illuminate\Cache\RateLimiter::class)->clear('other_'.$request->ip());
*: By counted together I mean they would add up, so if you apply two of them to the same request, each single request will bump the counter by 2!

ZF2: Where does the controller return/result get manipulated by the MVC?

I am working on a RESTful API in Zend Framework 2. I use the AbstractRestfulController to handle requests. In that controller I want to directly return an array or object (of a custom model class), not a JsonModel. That stuff and the conversion of the result to arrays (as a preparation for the JSON encoding) should happen later automatically.
I tried to catch the MvcEvent::EVENT_DISPATCH event to manipulate the $e->getResult()value but when I return an associative array in the controller I get a ViewModel in the result instead of the plain data passed in by the controller method.
I tried to create a custom view strategy by implementing the ListenerAggregateInterface class. In the ViewEvent::EVENT_RESPONSE event catch, I dumped the $e->getResult() value and got the already encoded result. Also here, I need the plain result to prepare it for encoding.
Where can I hook in to manipulate the controller's plain return value before encoding to JSON? Where does ZF2 generally manipulate the returned value in the MVC lifecycle?
Thank you in advance!
I don't have so much time to investigate in this issue, but my guess is that you may omit to stop event propagation when you attached a listener to MvcEvent::EVENT_DISPATCH. So what you set as the event result is probably overridden later in the dispatch process.
Try to attach your callback with a higher priority and stop the propagation ($e->stopPropagation() from the callback), since the event manager won't stop the propagation by itself until one or the other callback returns a Zend\StdLib\ResponseInterface object.
Anyway, I know you probably have good reasons trying to "hack" the dispatch process this way, but I tend to think that there could be even better reasons to adapt your code to fit the default process ;)
Here, zf2 creates a ViewModel in case an assoc array is detected in the result, registered at priority -80. According to this documentation page this is the first event catch changing the result returned by the controller.
What I did wrong is attaching my MvcEvent::EVENT_DISPATCH to the $application->getEventManager() instead of $application->getEventManager()->getSharedManager() (like zf2 does). I don't understand yet what the difference is, but it works.
Here is how I registered the event in the onBootstrap method:
$application->getEventManager()->getSharedManager()->attach(
__NAMESPACE__,
MvcEvent::EVENT_DISPATCH,
array($this, 'createJsonModelFromResult'),
-10
// priority of -10 because at priority 1 the mvc
// runs the actual controller method
);
At the end of the createJsonModelFromResult method I also added $e->stopPropagation(); because we already found the matching ViewModel.
Now the $e->getResult() value is exactly the same as returned in my controller actions and I can prepare it for JSON encoding. Cool!
I have no idea how you want to return an array or Object. Usually people make a DTO (Data Transfer Object), serialize it, and then send it. In this method the DTO at the end converts to JSON.
What format do you want to use in response? String (serialized DTO), XML, or something else? You can create a class extending ViewModel and serialize your array for your format.
Anyway, you do not need to get MVC_EVENT.

Symfony/propel form saving an object with additional properties

First, let me say, that I find the sfFormPropel form's interface inconsistent.
There is bind(), which returns nothing, but triggers validation, save() which returns the saved object, and bindAndSave(), which returns boolean, actually the return value of isValid(). Now, I have a working application, but I don't feel the code is right, and I'm quite new to symfony, so perhaps I'm missing something.
The object I need to create needs some external properties, that are not presented in the form, are external to the model, and are handled by the application (for example, the userId of the user, that created the entity, an external-generated guid, etc.).
Right now the flow is as follows:
get values from request and bind them to form
check if form is valid
if it's valid, add additional values and bind them to form one more time
save the form and return the object
The obvious answer would to add application-specific values to the values, retrieved from request, but It does not make sense to bind the application-specific values if the form is not valid, since they can be potentially expensive operations, may create database records, etc. Additionally, it should not be possible to pass those values with the post request, they should come from application only.
Now, I though that I have to let the model do these things, but since the data is external to the model, action still need to pass it to the model. The problem is, if I call $form->getObject() after bind(), it still has the old data, and not the data submitted.
What is the correct way to implement this kind of post-processing?
Second bounty is started to award the other valuable answer
The correct way would be setting your default values on the object you are passing to the form constructor. For example if you want to set the logged in user id on an object you are creating:
$article = new Article();
$article->setUserId($this->getUser()->getId());
$form = new ArticleForm($article);
if ($request->isMethod('post')) {
$form->bind($request->getParameter('article'));
if ($form->isValid()) {
$form->save();
}
}
Likewise for existing object, you can load the record and change any properties before passing it to the form constructor.
EDIT:
If you want to modify the object after validating, you can use $form->updateObject() like Grad suggests in his response. If the generated values depend on the submitted values, you can override sfFormObject::processValues():
class UserForm {
public function processValues($values) {
$values['hash'] = sha1($values['id'] . $values['username']);
return parent::processValues($values);
}
}
In case you need something from the action, you can always pass it as an option to the form:
$form = new UserForm($user, array('foo' => $bar));
That way, you can use $this->getOption('foo') anywhere in your form code, eg. in processValues().
It kind of depends of who has "knowledge" about the extra attributes. If they're really request specific, thus need to be processed in the controller, I go for binding, testing if valid and then update the bound object. To get the updated object with the bound (and validated) fields use the updateObject function.
$form->bind(..)
if ($form->isValid()) {
$obj = $form->updateObject(); // Updates the values of the object with the cleaned up values. (returns object)
$obj->foo = 'bar';
$obj->save();
}
But since this normally is also behaviour that is form specific, I usually go for overriding the Form class. By overriding the doUpdateValues() function you can easily access submitted data, and append your own data. Of course you can also go higher in the chain, and override the save() function.
To set custom data for this form, you can also 'publish' public methods, which can then be used by the controller.

Using objects in Ajax calls PHP files

I have instantiated a class in my index.php file. But then I use jQuery Ajax to call some PHP files, but they can't use my object that I created in the index.php file.
How can I make it work? Because I donĀ“t want to create new objects, because the one I created holds all the property values I want to use.
Use the session to save the object for the next page load.
// Create a new object
$object = new stdClass();
$object->value = 'something';
$object->other_value = 'something else';
// Start the session
session_start();
// Save the object in the user's session
$_SESSION['object'] = $object;
Then in the next page that loads from AJAX
// Start the session saved from last time
session_start();
// Get the object out
$object = $_SESSION['object'];
// Prints "something"
print $object->value;
By using the PHP sessions you can save data across many pages for a certain user. For example, maybe each user has a shopping cart object that contains a list of items they want to buy. Since you are storing that data in THAT USERS session only - each user can have their own shopping cart object that is saved on each page!
Another option if you dont want to use sessions is to serialize your object and send it through a $_POST value in your AJAX call. Not the most elegant way to do it, but a good alternative if you don't want to use sessions.
See Object Serialization in the documentation for more informations.
mm, you should store in session, $_SESSION["someobj"] = $myobj;, and ensure that when you call the Ajax PHP file this includes the class necessary files which defines the class of $myobj and any contained object in it.
Could you be more specific? I can try.
This is how I create an object then assign it to a session variable:
include(whateverfilethathastheclassorincludeit.php)
$theObject = new TheObjectClass();
//do something with the object or not
$_SESSION['myobject'] = $theObject;
This is how I access the object's members in my Ajax call PHP file:
include(whateverfilethathastheclassorincludeit.php)
$theObject = $_SESSION['myobject'];
//do something with the object
If you don't want to move your object that is in your index.php, have your ajax make a request to index.php but add some extra parameters (post/get) that let your index.php know to process it as an ajax request and not return your normal web page html output.
You have not provided code, but what I guess is that you need to make your instantiated object global for other scripts to see it, example:
$myobject = new myobject();
Now I want to use this object elsewhere, probably under some function or class, or any place where it is not getting recognized, so I will make this global with the global keyword and it will be available there as well:
global $myobject;
Once you have the object, you can put it into the session and then utilize it in the Ajax script file.
As others have suggested, $_SESSION is the standard way to do it, in fact, that was one of the reasons, that sessions where invented to solve. Other options, i.e. serializing the object rely on the client side to hold the object and then return it untampered. Depending on the data in the object, it is not a good solution, as a) the object may include information that should not be available on the client side for security reasons and b) you will have to verify the object after receiving it.
That said, and if you still want to use the object on the client side, then JSON is an option for serializing object data, see JSON functions in PHP.
Based on most of the answers here, referring to storing the object in $_SESSION, is it more efficient to store only the individual properties that need to be accessed in AJAX as opposed to the whole object, or does it not matter?
E.g.
$_SESSION['object'] = $object;
vs
$_SESSION['property1'] = $object->property1;
$_SESSION['property2'] = $object->property2;
I know the OP is asking about accessing the entire object, but I guess my question pertains to if it's just a matter of only accessing certain properties of an object, and not needing to access methods of a class to alter the object once it's in AJAX.

Categories