I have created a handler class that derives from AbstractProcessingHandler. I've seen that I can put it in src/MyNamespace/MyBundle/Monolog/, but it worries me a bit because this handler is used in several others bundles where I log data. So the other bundles will need MyBundle to work properly, only because of this handler.
I tried to put my handler class in lib/ but it does not seem to work (maybe I have to do something special with Autoload?).
Or should I create a new bundle specifically for this handler?
Edit: I can't really place my custom handler class in vendor/monolog/monolog/src/Monolog/Handler because then I would not be able to add it to my git repository: there is a conflict because this folder is managed by another git repository (created by Composer)
On Monolog's end there is really no restriction on where to put it or how you call it. The key is only that it implements monolog's HandlerInterface or extends from one of the existing handlers.
Now it depends what your handler is, if it's generic stuff that other people could use you could submit it as a pull request to monolog.
If not, you can either create an own composer package for it, or put it in src/Acme/Monolog/FooHandler or something like that, so it stays in your application but is clearly out of a bundle. The downside is that you need to configure it as a service in one of your bundles, so you still have some sort of dependency on a bundle there.
Maybe having it as its own bundle would make sense then. But it's quite a lot of boilerplate for just one class.
If all your bundles are application specific and very unlikely to be extracted out of it, having cross-bundles dependencies is fine though IMO.
The dependency is anyway not very strong since one bundle could contain the handler and configure it. The other bundles can still log to monolog, even if the handler isn't present, they can log. It just won't go to that specific handler. Nothing should break.
As you see, it's just a lot of trade-offs, and it's hard to say which solution is the most fitting without knowing more about your project.
If you want to have your handler class in lib/ you will need to add the lib/ folder to your composer.json autoload section. For example:
"autoload": {
"psr-0": { "": ["src/", "lib/"] }
}
Take a look at the Composer documentation:
Basic Usage
Autoload
I think the common approach here is to use a "Bridge" dir in your Bundle with a clear dependency. If you have other bundles that rely on this, what we've done is create a ServiceBundle which is basically for all shared services across all bundles within the application. This might not work well for you if you have plans of distributing this bundle, but may otherwise.
Related
I am working on very old legacy code, mostly procedural. Trying to improve it. Rewriting applications is impossible right now. The plan is to add a few libraries which would help organize things and improve that way.
I added a Symfony dependency-injection component in order to do that. It would provide the possibility to fetch needed services with its dependency easy.
I watched symfonycast tutorial on how to play with container. And with that knowledge, I managed to write a simple loader to start the container and to use services made by me. It is simple, it guesses FQCN based on file path, and then uses reflection to get dependencies. But I can not figure out how to load vendor classes, because here you can not guess namespace that way. :)
The question is: What exactly Symfony uses to load classes from the vendor folder, does it reads composer.json files to see namespaces, does it uses some composer feature, or something else?
Loading classes is different than instancing services.
The first can in fact use regular composer facilities to discover vendored classes in a legacy project like yours, even if they weren't installed with composer. This uses the standard php autoload mechanism with some added magic.
To include the, let's say lib/ legacy directory in the discoverable files you would add the following to composer.json:
"autoload": {
"classmap": ["lib/"]
}
And then run composer dump-autoload. Note that by including vendor/autoload.php in your legacy files you could even forego the require directives for your dependencies and rely on composer as well. This can be a path for migrating them to composer-managed dependencies, too.
Service instancing requires not only being able to locate the classes themselves, but also their respective dependencies so the container can create the object tree automatically. This usually involves hand-writing service definition files: classes in the vendor/ folder are not automatically registered as services. A bundle (or your own definitions) enables support for an specific library.
Take for instance the Mailer component: you can use it as a standalone library, but for framework integration (which includes service definitions and depen) you'd need to install Mailer bundle as well.
The exception where automatic service registration applies (when using symfony framework, not the standalone dependency injection component) is for files under src/. During container compilation, services.yaml is loaded and the ContainerConfigurator with help from FileLoader, looks for *.php files the directories configured as a resource, creating service definitions for them.
I guess you could do a similar thing for your legacy dependencies in a CompilerPass by using a similar technique or by trying to leverage the composer classmap but, specially if your legacy dependencies do not follow a PSR loading standard, I'd advise against it, since it can pull in tests, example files, etc.
One simple service offers an API to use some of its features. I want to create a composer package which will consume the following API and I want it to be compatible with other PHP projects. I read about the topic and came up with the idea to to use GuzzleHttp to make the requests (I saw it in few other libraries). However I'm confused about the structure of an API consuming library. (It's a REST API).
The API gives access to two resources: Customers and Products.
Products resource has the following methods:
List all available products - GET
Add products - POST
Delete product - DELETE
Customers resources has the same methods.
What I've done so far is the following structure (I'm following psr-4 as suggested):
src/
--MyName/
----PackageName/
------Resources/
------Containers/
------Exceptions/
------Client.php
src/MyName/PackageName is the structure I read in a tutorial about creating a composer package. MyName\PackageName will be my namespace throught it.
File Client.php is a class which loads some configuration about authorization (Basic Auth) and creates new instance of GuzzleHttp\Client. Also I have two methods for building a request (setting HTTP Method, URL & additional parameters).
Also I have a __call() method which instantiates new object from Folder Resources and the first element of the array passed as second argument is the method which should be called.
Folder Resources contains two files Products.php and Customers.php which are classes for handling all methods for those two resources I mentioned above. Every class extends Client.php.
Folder Containers contains files for processing the response data from every resource.
Folder Exceptions contains classes for custom exceptions which might be thrown in the process.
Is that a good approach to a easily maintainable library or I'm missing some of the concepts here?
How to structure a composer package
To cut a long story short: stick to PSR-4, decide for a folder layout and expose this layout in the autoloading section of your composer.json file. The rest is: pick clear and understandable class and method names to expose the API.
Your question is a mix of different things and some things overlap each other.
When talking about the structure of your project, we have to split between source (object-oriented design) and the folder layout (autoloading relevant) and the composer integration (with autoloading description).
Let's go through this in order...
a) source-code
However I'm confused about the structure of an API consuming library.
Is that a good approach to a easily maintainable library or I'm missing some of the concepts here?
The questions is: Is my code clear and precise enough (for myself and for others, e.g. to be consumed in another project)?
The advises to give here are:
Picking clear class and method names makes use easier, like Company\PhotoApi\Client.php
Namespace and Class could probably expose the vendor, e.g person or company producing the source and then you could include the API name, finally the classname.
Follow some basic OO principles
How you fetch the data from the API is taste based. Going with Guzzle is ok, while going with a lighter solutions, like file_get_contents or curl, would probably work out, too. It depends.
in regard to maintainability:
The lower the number of files and the less complex your code is, the better the maintainability. Do you need an Exceptions folder? How many files are there? Why not simply stick to PHP's default exceptions?
You might also consider, that your lib has to change if the API changes, right? And, then if your lib changes, all the code in projects using your lib has to change, right? If there is only a small set of endpoints exposed by the API, then it might be better to use just one object and provide accessor methods for them, instead of using multiple objects as accessors, which might be too fine-grained. This means that projects using your API will (possibly) have fewer lines changes when upgrading. Anyway: your users will tell you, if your API lib is too difficult to use.
While you have something like:
$PhotoApiProducts = new Company\PhotoApi\Products;
$products = $PhotoApiProducts->get();
This is also possible:
$api = new Company\PhotoApi\Client;
$products = $api->getProducts();
$consumers = $api->getConsumers();
b) folder structure
I'm following psr-4 as suggested
In regard to the folder layout, my suggestion is to stick with PSR-4.
But you have to decide the exact folder layout yourself. You might take a look at the examples section of the PSR-4 standard to see different folder layouts respecting PSR-4. http://www.php-fig.org/psr/psr-4/
c) Composer integration
And then finally.. you add a composer.json file describing your project.
The autoloading section is the most important part, because this is were you expose the structure of your project to Composer.
When you have decided to use PSR-4 for your project, then simply say so in the autoloading section and add the mapping from your namespace to source, like
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/Foo/Bar/"
}
}
Now, a user of your Composer project has to load the composer autoloader during the bootstrap and then he might start using your lib, by just using a namespaced classname from it - then the autoloader will do its work.
currently I'm thinking on how to implement a plugin-feature for my symfony2 application.
My goals are as following:
Basically the plugin should be a bundle (at least service, event listener, routing, entities, migrations, views and controllers need to be registerable)
The AppKernel should not be touched in any way by the end user or the application
Plugins should provide meta information (title, description, screenshot/image, author etc)
plugins should be managed from the backend (activation, deactivation, installation, deletion, update etc)
plugins may come from different sources (Core, Community, Local as in shopware maybe) and should be seperated by save path
working autoload without running composer again
My problem would be something like: How to load a bundle after the Kernel is already loaded (since the information on which plugins are active should reside inside the database ideally and not inside some file on the hard disk but may be cached).
I did not found a bundle or a cook book entry on this topic. Is there a best practice managing something like this? Maybe I'm thinking in the wrong direction.
I hope there is someone who might have a helpful idea and the kind of knowledge on symfony2 which I do not have.
PS: There is no code yet since this is just some thinking on the concept on how to handle this.
The AppKernel returns the registered bundles as an array, so assuming that the storage method you're using to determine active plugins isn't tied to Symfony (i.e. you can access it before the application is fully bootstrapped), then it should be as easy as adding something like this to your AppKernel.php:
public function registerBundles()
{
...
...
$bundles = array_merge($bundles, MyApp\PluginManager::getPluginBundles());
...
return $bundles;
}
Providing the plugin metadata could be done in various ways (maybe just a defined location / directory in the plugin files that holds txt / image files), but loading all that should be easy once you have the plugin management stuff (and can easily get the installed plugins, etc) done.
I feel like I have searched the entire internet through and through, but cannot seem to figure this one out. I'm using Silex (the latest version) and cannot seem to figure out how to use Silex's ServiceProvider system to return an instance of a class for use.
I do know how to create a basic service provider.
What I do not know how to do is how to have this service provider use a custom class. I've tried everything I can think of or find on the web. Part of the problem is that Silex's documentation on this is not very extensive, and most of the questions that have been ask about this type of issue were asked/answered before a pretty large change to how it was done (or so it seems) so the answers are not current.
So, put briefly:
I want to use Silex's $app['myclass'] type system to access my class, so that I can do things like $app['myclass']->myMethod().
Where I'm hung up is this, while I can create a service provider, I can't figure out how to get the service provider to recognize the class. I've tried the whole namespace thing with the composer auto-load pso-0 set up, and have tried use MyClass/MyClass type things.
Haha, basically, because there is so little documentation, there could be any part of it that I am doing wrong.
Would someone write a current step-by-step process for hooking up a custom library/class to the $app variable? I think this would help not only me, but also others. Thanks!
Seems to me like you are having issues with class loading. This is something that used to be handled by the autoload service in silex. That service was removed however, in favour of composer's autoloading.
You were on the right track with specifying the autoloading in composer.json. In case you're not familiar with composer, read the introduction. For details on how the autoloading works, see the autoloading section of the basic usage chapter.
I will give you the short version here. Make sure your filenames comply with the PSR-0 naming standard. Add this to your composer.json:
{
"autoload": {
"psr-0": {"Acme": "src/"}
}
}
You need to replace Acme with your namespace and src with the base directory for your classes. For example, the class Acme\Foo\Bar would be located in src/Acme/Foo/Bar.php.
Then run php composer.phar update to get the autoload files re-dumped and you should be able to access your classes.
So that the behavior template can be autoloaded when necessary.
I'm confused by the complex settings.
I put them in lib/doctrine, and if there's a listener as well, put that in lib/doctrine/listener.
Symfony's autoloader will find the classes and load them for you pretty much wherever you put them (as long as its vaguely logical). The only exception is if you create a new vendor folder, you will need to tell the autoloader to check there as well.