Is it possible to serialize an object into data which will be passed to the compiler pass? - symfony

I have a symfony bundle which needs to store a lot of data. Currently said data is stored as arrays, read and put into the memory of a service. While this approach works, it's not very readable.
It's not possible to simply set an argument to a list of X, as such I'm unable to store said data in a better way.
My data will not change between cache compiles, so I was wondering if there's anything where I can do something like the following
class MyModel {
public function __construct(
public readonly string $from,
public readonly string $to
) {}
}
// on compiler pass
$model = new MyModel('aaa', 'bbbb');
$myDefinition->addMethodCall('myMethod', [[$model], $otherScalarArguments]);
I was also wondering if Symfony's expressions could help in this case, but I'm unsure.
My current use of ->addMethodCall has 7 arguments, so this would make my life easier.

At first I thought the container could handle this sort of thing so I reproduced the issue and got:
Unable to dump a service container if a parameter is an object or a resource.
So even though you can pass an object as an argument, the caching code which writes the compiled container out as a php file is not smart enough to handle it. Note: it is often useful to add error messages to questions.
The trick is to serialize the object in the pass itself and then unserialize it in the service.
class MyService
{
private MyModel $myModel;
public function __construct(string $myModel)
{
$this->myModel = unserialize($myModel);
dump($this->myModel);
}
}
# pass
$model = serialize(new MyModel('aaa', 'bbbb'));
$def = $container->getDefinition(MyService::class);
$def->addArgument($model);
I tested it and it all seemed to work. I did take take the liberty of simplifying the original code a bit but it will work work just fine for addMethodCall as well.
Might be worthwhile proposing this to the Symfony core group. It is niche functionality but might come in handy now and then.

Related

Dart: Code design for interface to construct objects from HTTP requests

I'm trying to implement an abstract base class for objects that will be parsed from HTTP calls. The goals is to create a CachedFuture<T> class that, when accessed, makes an HTTP call to get the object and caches the result.
My inclination was to implement the classes as follows:
class HTTPGetable<T extends HTTPGetable<T>> {
static Future<T> httpGet(int id);
}
class CachedFuture<T extends HTTPGetable<T>> {
int _id;
Future<T?>? _cachedResult;
bool _has_already_made_request;
CachedFuture({required int this._id});
Future<T?> get() {
if(this._cachedResult == null)
try {
this._cachedResult = T.httpGet(this._id);
} except {
// parse error or other error
this._cachedResult = CompletableFuture.completedFuture(null);
}
}
return this._cachedResult;
}
}
The intuition here is that there are a lot of objects with "pointers" to other objects in the database on the backend. It doesn't make sense to follow every pointer when constructing objects to return in the HttpRequest, so my intent is to wrap "pointers" in this CachedFuture class.
This should enable someone to "follow" one of these "pointers" without ever having to worry about caching the http request or about the specifics of constructing that request. Hopefully this provides another level of abstraction for the user of the class so they can right code that's agnostic to what's going on under the hood.
However, static interfaces aren't in Dart, so this pattern doesn't work. My intuition is that this is a sign I should be using a different pattern instead of trying to force my way.
httpGet is basically a factory method, but the functionality of CachedFuture needs a way to look up the factory from the type, so I don't immediately see a good way to implement a true factory either.
Could anyone recommend a good programming pattern for solving this? I imagine it's a pretty common usecase for both http requests and database requests.
Update: The best workaround I've found is to do different "factories" to httpGet each T, then use reflection to look up the appropriate factory. However, as the description sounds, this seems pretty convoluted & error prone, so I'm still convinced that there's almost certainly a better pattern to use.
Can't you just create the abstract class CachedFuture along the lines of what you wrote, with an abstract method httpGet(int id) to be implemented by the subclass?
If there is only ever one instance of an object of type <T extends CachedFuture> then you could consider making the subclass a Singleton.
Apologies if I misunderstood your question!

Differences between different methods of Symfony service collection

For those of you that are familiar with the building of the Symfony container, do you know what is the differences (if any) between
Tagged service Collector using a Compiler pass
Tagged service Collector using the supported shortcut
Service Locator especially, one that collects services by tags
Specifically, I am wondering about whether these methods differ on making these collected services available sooner or later in the container build process. Also I am wondering about the ‘laziness’ of any of them.
It can certainly be confusing when trying to understand the differences. Keep in mind that the latter two approaches are fairly new. The documentation has not quite caught up. You might actually consider making a new project and doing some experimenting.
Approach 1 is basically an "old school" style. You have:
class MyCollector {
private $handlers = [];
public function addHandler(MyHandler $hamdler) {
$handlers[] = $handler;
# compiler pass
$myCollectorDefinition->addMethodCall('addHandler', [new Reference($handlerServiceId)]);
So basically the container will instantiate MyCollector then explicitly call addHandler for each handler service. In doing so, the handler services will be instantiated unless you do some proxy stuff. So no lazy creation.
The second approach provides a somewhat similar capability but uses an iterable object instead of a plain php array:
class MyCollection {
public function __construct(iterable $handlers)
# services.yaml
App\MyCollection:
arguments:
- !tagged_iterator my.handler
One nice thing about this approach is that the iterable actually ends up connecting to the container via closures and will only instantiate individual handlers when they are actually accessed. So lazy handler creation. Also, there are some variations on how you can specify the key.
I might point out that typically you auto-tag your individual handlers with:
# services.yaml
services:
_instanceof:
App\MyHandlerInterface:
tags: ['my.handler']
So no compiler pass needed.
The third approach is basically the same as the second except that handler services can be accessed individually by an index. This is useful when you need one out of all the possible services. And of course the service selected is only created when you ask for it.
class MyCollection {
public function __construct(ServiceLocator $locator) {
$this->locator = $locator;
}
public function doSomething($handlerKey) {
/** #var MyHandlerInterface $handler */
$handler = $serviceLocator->get($handlerKey);
# services.yaml
App\MyCollection:
arguments: [!tagged_locator { tag: 'app.handler', index_by: 'key' }]
I should point out that in all these cases, the code does not actually know the class of your handler service. Hence the var comment to keep the IDE happy.
There is another approach which I like in which you make your own ServiceLocator and then specify the type of object being located. No need for a var comment. Something like:
class MyHandlerLocator extends ServiceLocator
{
public function get($id) : MyHandlerInterface
{
return parent::get($id);
}
}
The only way I have been able to get this approach to work is a compiler pass. I won't post the code here as it is somewhat outside the scope of the question. But in exchange for a few lines of pass code you get a nice clean custom locator which can also pick up handlers from other bundles.

SonarQube complains about using ResponseEntity with a wildcard

I use SpringBoot for REST web services development and SonarQube for static analysis.
I have a few endpoints in my application that look the following way:
#PostMapping
ResponseEntity<?> addSomething(#RequestBody Some object) {
// some code there
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
SonarQube complains about using ResponseEntity with a wildcard, reporting me a Critical issue "Generic wildcard types should not be used in return parameters".
I wonder if I should disable this verification in SonarQube or come up with something different for return type for these cases.
What do you think about it?
#PostMapping
ResponseEntity<Object> addSomething(#RequestBody Some object) {
// some code there
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
This will also remove the error. It is still very generic, but it is one of the solutions if you want to return different types based on the outcome. For instance:
#PostMapping
ResponseEntity<Object> addSomething(#RequestBody Some object) {
//Will return ResponseEntity<> with errors
ResponseEntity<Object> errors = mapValidationService(bindingResult);
if (!ObjectUtils.isEmpty(errors)) return errors;
// some code there
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
So actually i find the rule pretty self describing:
Using a wildcard as a return type implicitly means that the return value should be considered read-only, but without any way to enforce this contract.
Let's take the example of method returning a "List". Is it possible on this list to add a Dog, a Cat, ... we simply don't know. The consumer of a method should not have to deal with such disruptive questions.
https://sonarcloud.io/organizations/default/rules#rule_key=squid%3AS1452
So Actually in your case, you do not want any kind of Class in there, you specifically want an Serializable-object - for obvious reasons: it should be serialized later on
So instead of using ? it would be more suitable in your case to use Serializable. This is always case dependent, but normally you definitly expect some kind of common interface or base class as a return value. Hence that, the follow up developer, definitly knows what he can expect, and what kind of functionality he definitly can use.
Finally I've removed <?> from return value, so the code looks like the following now:
#PostMapping
ResponseEntity addSomething(#RequestBody Some object) {
// some code there
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
SonarQube doesn't complain anymore and code seems a little bit simpler now.

What is the main advantage to use service in Symfony?

I'm a newbie on symfony, And I don't understand the advantage of use service instead of write the cose in Controller
For Example, I Have a service that Create Log, with a code like this:
$path = $root.'/../web';
$fs->touch($path.'/log.txt');
$this->file = $path.'/log.txt';
file_put_contents($this->file, $msg, FILE_APPEND | LOCK_EX);
I can put this login in service with DIC ($fs is FileSystem service), or I can Put this Login on my Controller.
Of course If i Need to log often I have to write the same code. The main advantage is decoupling?
Thanks a lot
Suppose you have a Bar class which uses BasicLogger.
You have a few ways to get access to this logger, lets start with the most simple option:
<?php
class Bar
{
public function bar()
{
$logger = new BasicLogger();
$logger->log("foo");
}
}
This is bad practice because we are mixing construction logic with application logic. It still works, but it has the following drawbacks:
It mixes responsibilities.
Bar becomes hard to test and cannot be tested without side effects.
We cannot dynamically change loggers (code is less reusable).
To solve these drawbacks, we can instead require our Logger class through the constructor.
Our code now looks like this:
class Bar
{
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function bar()
{
$this->logger->log("foo");
}
}
Great, our class is no longer responsible for creating the logger, we can test our code without side effects (and make assertions against how the logger was used) and we can now use any logger we like.
So now we use our new class all over the application.
$logger = new Logger();
$bar = new Bar($logger);
Look familiar?
Again we are mixing construction logic with application logic, which we already know is bad.
Not only that, but something even worse is happening here, Code duplication.
Thats right. and every time we want to use our Bar class, the duplication gets worse.
The solution? Use the Service container
Registering your logger as a service would mean that all of your code that needs logging functionality is no longer dependent on your specific logger, responsibilities will not be mixed, code duplication will be reduced and your design will become more flexible.
The main goal and advantage of services is that keep reusable code and use a DRY approach.
Of course, there is a lot of other advantages that you discover progressively as you use them.
Services are accessible from whatever context of your application that can accesses the service container, not only controllers.
If without the service the few lines of code you give would be duplicated in several methods/contexts, you should keep your service.
Otherwise, delete it and do your logic in the specific method.
I think the better approach to use them is at your own feeling.
Don't try to create services in prevention, use them to solve a real need.
When you have a block of code that is duplicated, you should naturally avoid it by creating a service (or other AbstractController that your controllers can extend and inherit the code block) .
The goal is: Always keep a light code and avoid duplicates as possible.
For that, you can use the powerful services of Symfony, or just use the inheritance of classes and other POO principles.

Unity fluent registration - can this be any shorter?

If I have a class that takes in several constructor arguments including a string that can be null, I am currently using the following syntax for registering it:
container.RegisterType<ISomething, Something>(
new InjectionConstructor(new InjectionParameter<string>(aString), typeof(ISomethingHelper), typeof(ISomethingManager)))
I added InjectionParameter to cope with the case of a null aString which Unity complained about without this.
Is all this necessary or can I shorten it a bit?
Nulls are a bit of a special case, since we can't derive a type from a constant of type null (it just comes through the compiler as type object). So short of writing a small helper function / class, yeah, that's as short as it gets.
I could see writing something like this:
public static class NullParam
{
public InjectionParameter OfType<T>()
where T : class
{
return new InjectionParameter<T>(null);
}
}
Then you could write the above as:
container.RegisterType<ISomething, Something>(
new InjectionConstructor(
NullParam.OfType<string>, typeof(ISomethingHelper), typeof(ISomethingManager)));
I'm not sure that's shorter enough to be worth the introduction of the helper.
The Unity API is designed around regularity and extensibility, not brevity or convenience. This does make some things wordier than would be ideal. The nice thing is that it's pretty easy to write little wrappers and helpers to make the registration code look the way you want it to.

Resources