Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[HttpKernel] AddMapSessionParameter to map session parameters to controllers#54458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
jtattevin wants to merge3 commits intosymfony:7.4
base:7.4
Choose a base branch
Loading
fromjtattevin:MapSession

Conversation

jtattevin
Copy link

@jtattevinjtattevin commentedApr 2, 2024
edited
Loading

QA
Branch?7.3
Bug fix?no
New feature?yes
Deprecations?no
LicenseMIT
Doc PRsymfony/symfony-docs#19728

This attribute allow to map an object from/to the session. The read/write to the public properties are proxied to the session using the property name as a key, or the value defined by the attribute SessionKey. There is no prefix, so if two class use the same property, they will read/write to the same session entry, which is intended to have variable shared between context if needed.

This allow to type property and have ide autocompletion, static validation and runtime validation of type. The default value from the property is also used as a default value when reading from session.

Using this method reduce the errors related to a wrong key used or inconsistent default value. It also help seing which key are currently used and avoid conflicts

Calling isset will return if the property is defined in session but will not check if the value is null. Calling unset remove the entry from the session

For example, we can create a class like this :

class BrowsingSessionContext{    public string $locale = "en";    public bool $preferDarkTheme = false;}

and use it in a controller :

#[Route("/", name: "home")]class DefaultController extends AbstractController{    public function __invoke(        Request $request,        #[MapSessionContext] BrowsingSessionContext $browsingSessionContext,    ) {        $localeSelector                 = $this            ->createFormBuilder($browsingSessionContext)            ->add("locale", TextType::class)            ->add("preferDarkTheme", CheckboxType::class)            ->add("submit", SubmitType::class)            ->getForm()        ;        $localeSelector->handleRequest($request);        if ($localeSelector->isSubmitted() && $localeSelector->isValid()) {            return $this->redirectToRoute("home");        }        return $this->render("index.html.twig", [            "value"          => $browsingSessionContext->locale,            "localeSelector" => $localeSelector,        ]);    }}

The term session context come from the idea that we get a lot of information that are often together. I avoided to use something too close of Object to avoid confusion with the SessionInterface object.

This pull request is based in HttpKernel to be close toSessionValueResolver but i'm not confident if the namespaces used are the right ones.

I'm not fond of the eval to create the anonymous class, but didn't managed to create an anonymous class without that. Also, i'm not sure if it's possible to create a proxy during container build in this context.

valtzu reacted with thumbs up emoji
@carsonbot
Copy link

Hey!

I see that this is your first PR. That is great! Welcome!

Symfony has acontribution guide which I suggest you to read.

In short:

  • Always add tests
  • Keep backward compatibility (seehttps://symfony.com/bc).
  • Bug fixes must be submitted against the lowest maintained branch where they apply (seehttps://symfony.com/releases)
  • Features and deprecations must be submitted against the 7.1 branch.

Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change.

When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor!
If this PR is merged in a lower version branch, it will be merged up to all maintained branches within a few days.

I am going to sit back now and wait for the reviews.

Cheers!

Carsonbot

@carsonbot
Copy link

Hey!

Thanks for your PR. You are targeting branch "7.1" but it seems your PR description refers to branch "7.2".
Could you update the PR description or change target branch? This helps core maintainers a lot.

Cheers!

Carsonbot

@carsonbotcarsonbot changed the title[WIP] [HttpKernel] AddMapSessionContext to map multiple session value to…[HttpKernel] [WIP] AddMapSessionContext to map multiple session value to…Apr 2, 2024
@jtattevinjtattevin changed the title[HttpKernel] [WIP] AddMapSessionContext to map multiple session value to…[HttpKernel] AddMapSessionContext to map multiple session value to…Apr 2, 2024
@jtattevin
Copy link
Author

jtattevin commentedApr 3, 2024
edited
Loading

Status: needs review

@nicolas-grekas

This comment was marked as outdated.

@jtattevin
Copy link
Author

Thank for the feedback.

I didn't think about injecting the object itself, i think it would work yes and be simpler.

Initially i made it this way to migrate a project that was using directly the set/get without constant, so the goal was mainly to proxy the calls and avoid issues like reading 'locale' but writing to '_locale'. In session i wanted to have the individual keys.

I'm not sure which method would be better

@nicolas-grekas
Copy link
Member

Not using proxies is better for sure :)

@jtattevin
Copy link
Author

Update done, i've removed the magic trait and also the possibility to specify the session key for each property.
The class FQDN is the entry in the session

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I like this idea :)

@nicolas-grekasnicolas-grekas changed the title[HttpKernel] AddMapSessionContext to map multiple session value to…[HttpKernel] AddMapSessionParameter to map session parameters to controllersApr 4, 2024
@jtattevin
Copy link
Author

Update done, good idea to allow the interface and thank for all the feedback.

I made a change on which i'm not 100% certain if it was the right call :
If you type an interface, you need to make the parameter nullable or provide a default value.

While it is not always mandatory because if there is already a value in session, it would work without a default value, it may lead to an error later if the controller is called without setting the session before. (For example, the server restart and clear the session).

This is a case that may be complicated to detect during development, so trigger the error even if it was possible to continue without error.

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This looks good to me, I only have CS-related comments.

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I like this PR because it enables more decoupling from framework-provided interfaces (SessionInterface here).

Any other opinions on the topic @symfony/mergers? Votes pending 🙏

Copy link
Member

@lyrixxlyrixx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Is it a commun thing to store object in session?
It feel a bit wrong to me.
People need to deal with BC between 2 deploys...

return [];
}

if ((!$type = $argument->getType()) || (!class_exists($type) && !interface_exists($type, false))) {
Copy link
Member

@lyrixxlyrixxAug 22, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

  1. We should deal with nullable type?
  2. It does not work with union / intersection type

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Allowing null allow to use an interface or to create the object only if needed.

I didn't checked for the union type, i'll update the PR

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

compound types aren't compatible with value resolvers IMHO, this one or any other

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For the record, I tested, an it fails indeed. It tells the class"App\Foo|AppBar" does not exist

return [$value];
}

if (\is_object($value = $argument->hasDefaultValue() ? $argument->getDefaultValue() : new $type())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't understand why there is a if is_object here

  1. $argument->getDefaultValue() is of type object, expect if null are allowed, but this is weird?
  2. new $type is an object too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What is there is a constructor with required arguments?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The is_object check if the default value is null or not, if it's null, we don't write in session.

If you have a constructor with arguments, you'll have to provide the default value in the controller :

public function index(        #[MapSessionParameter()] Foobar $foobar1 = null,        #[MapSessionParameter()] Foobar $foobar2 = new Foobar("myParam"),    ): Response

@lyrixx
Copy link
Member

lyrixx commentedAug 22, 2024
edited
Loading

And I just tested the PR,

  1. it keeps writing to the session, event if nothing changes
class HomepageControllerextends AbstractController{    #[Route('/')]publicfunctionindex(        #[MapSessionParameter()]Foobar$foobar4,    ):Response    {dump($foobar4);return$this->render('homepage/index.html.twig', [        ]);    }}class Foobar{publicfunction__construct(    ) {    }}
  1. I brokenALL FPM worker many time by playing with types. I ransymfony:server and had to restart it each time

@jtattevin
Copy link
Author

Is it a commun thing to store object in session? It feel a bit wrong to me.

The goal of this PR is not to allow storing object but reducing type error and wrong session key error (username vs userName).
If you expect to have an int in session, the property of the object or the method on the interface will require you to provide the right type.
And while the typo part is a little less present in the current version, as it depend mainly on a variable name, it may be easier to spot.

People need to deal with BC between 2 deploys...

Didn't really think about this point. I guess you can use the fact that there is support for interface to leverage this point.
In my mind you have only one or two property per object, so there should not be too much issue with BC, at least not new issues compared to calling session->get directly

@lyrixx
Copy link
Member

My point about objects it that is encourage people to store objects in the session, and IMHO this is a terrible idea. So, even if this PR looks nice at glance, it's not a good idea.

@nicolas-grekas
Copy link
Member

But we do ourselves put value objects in the session, I see no issue, with some care of course.

@jtattevin
Copy link
Author

I've added a new commit to add support for the union/intersection type, they are considered like the interface (need to provide a default value or make them nullable).
This also fix if you usedFooInterface|null $var instead ofFooInterface|null $var = null

There is one point that i didn't find an easy/effective way to do : Check if the data in the session match the expected type to trigger an exception saying that the type in session was not compliant. Instead, the user will have a message like
Argument #N ($browsingSessionContext) must be of type (Foo&Bar)|null, Baz given

Is there a method like that ? (instanceof and is_a doesn't seem to handle union types)
Or can we leave the php default error ?

… to a controller argumentThis attribute allow to pass an object from the session.The read/write to the public properties are proxied to the session using the property name as a key, or the value defined by the attribute SessionKeyThis allows to type property and have ide autocompletion, static validation and runtime validation of type.The default value of the argument is also used as a default value when not defined in session.In case of interfaces, the user will be required to provide a default value or make the parameter nullable.This check is done even if the session may have a value already to notify quickly of the potential issue
@GromNaNGromNaN added the ❄️ Feature FreezeImportant Pull Requests to finish before the next Symfony "feature freeze" labelApr 1, 2025
@smnandre
Copy link
Member

Two very genuine questions about this attribute, sorry if it has been mentionned in one of the previous threads.

First: Isn't this the only valueresolver thatwrite things? If so I guess this is something that should be written in bold in the documentation.

Second (related) question: does this mean if a controller is protected by aIsGranted attribute --as the request arguments have to be resolved before calling the authorisation request listener-- with this PR the resolver will save data in sessionbefore authorisation. In case of access denied, or any error in fact... the value is not rollback to its previous state, or did I miss something ?

@chalasr
Copy link
Member

First: Isn't this the only value resolver that write things? If so I guess this is something that should be written in bold in the documentation.

This is not desired to me, I would remove theSession::set() call.

@jtattevin
Copy link
Author

Two very genuine questions about this attribute, sorry if it has been mentionned in one of the previous threads.

This was not mentionned before, and for the second question, i have not a clue, so i'll dive more into this part, so thank you :)

First: Isn't this the only valueresolver thatwrite things? If so I guess this is something that should be written in bold in the documentation.

It may be the only resolver that write, it was to avoid the case where depending of if the session was already set or not, the value would save automatically or not.
If the behavior stay like this, the documentation will be updated to indicate this part.

Second (related) question: does this mean if a controller is protected by aIsGranted attribute --as the request arguments have to be resolved before calling the authorisation request listener-- with this PR the resolver will save data in sessionbefore authorisation. In case of access denied, or any error in fact... the value is not rollback to its previous state, or did I miss something ?

I don't know enough about the exact order for this process, i'll deep dive into this part to understand more.
In this case, either you already have a value, then the value is only read from session, or you don't, then a default value is written in session. It can break something if you depend on thesession->has.

From all the feedback, i get that the fact that it is an object that is stored and we write automatically in the session is not the intended behavior.
We can either rollback to the original version where the class was proxied to call session->get/set instead of reading properties.
Or make the autowire attribute work only with argument that have default value or nullable with no auto-set, so the user should always be saving manually and handle creation (maybe allow to autowire the session or the setter).

@fabpotfabpot removed the ❄️ Feature FreezeImportant Pull Requests to finish before the next Symfony "feature freeze" labelMay 17, 2025
@fabpotfabpot modified the milestones:7.3,7.4May 26, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

@lyrixxlyrixxlyrixx left review comments

@jdreesenjdreesenjdreesen left review comments

Assignees
No one assigned
Projects
None yet
Milestone
7.4
Development

Successfully merging this pull request may close these issues.

10 participants
@jtattevin@carsonbot@nicolas-grekas@lyrixx@smnandre@chalasr@jdreesen@fabpot@GromNaN@xabbuh

[8]ページ先頭

©2009-2025 Movatter.jp