override symfony 3rd party bundle resource - php

Symfony 2.7.5
Problem
I'm trying to override a class file in a 3rd party vendor file so that I can extend the functionality a little. This file isn't part of a service, or a controller.
Rather confusingly (I'm not sure why this worked), I've over-ridden part of FOSUserBundle by creating the directory structure as follows:
src
│
│
└───FOS
│
│
├───Model
│ User.php
│ Group.php
│ ...
This has allowed me to change the visibility of some of the FOS user class members. And it allowed me to do with this without any other config. Not even a child-class set-up.
Tried so far
I've read this, but it only talks about basic resources.
I've also read this, but it seems to only work for controllers or anything in the Resources folder.
Ive tried setting up a child bundle, similar to the above link but to no avail.
I cant think of a way of successfully using inheritance in this instance to accomplish what I need to do. As the file in question isn't referred to outside of the vendor bundle itself.
Question(edited)
Is there a way that I could extend/override a bundle class file that lays in a directory structure such as this? I don't need to touch much of the bundle, but I really need to get at this file. This is an extract from APYDataGridBundle.
vendor
│
│
└───APY
│
│
├───datagrid-bundle
│
│
├───Grid
|
| .. grid.php <-- this file

You can do this by using Composer’s autoload feature.
Composer, as you might already know, will automatically load the PHP file containing a class whenever you first call that class. So you don’t have to use require statements all the time.
To achieve what you’re looking for, you can “trick” the autoloader into loading a different file whenever APY\DataGridBundle\Grid\Grid is called.
How to do that
In your composer.json there should be a section like this:
"autoload": {
"psr-4": {
"": "src/"
}
},
This will tell Composer that your classes are in the src/ directory. But since APYDataGridBundle already defined a more specific namespace path in their composer.json, the autoloader will never look at your files.
If you change the block like this:
"autoload": {
"psr-4": {
"": "src/"
},
"classmap": ["src/APY/DataGridBundle/Grid/Grid.php"]
},
Composer will analyze the file Grid.php and look for classes inside it. It will then create a class map so that whenever the class inside it is called, it will know which file to load before even checking the PSR-0 or PSR-4 namespace paths.
So basically, you’re telling it the path to the file at a higher priority so it gets loaded first.
Of course, you don’t even have to match the namespace with your directory structure, you could also do something like src/replacements/grid.php if that’s better for you. Just make sure that the namespace declaration inside the file is the right one.
Edit: After changing composer.json you have to execute composer dump-autoload so the new autoload files get generated based on the new config.

In general, you will only be able to override a class in a bundle in one of two cases:
If symfony has defined a generalized way to look for these classes so that you can override them just by putting a new class in a directory. This happens with controllers, for example.
If the bundle loads this class by using a parameter in its service definition. In this case you can override it by setting this parameter in your config file
In your case you are lucky and this bundle loads this class through a parameter defined in its services.xml file, so you can provide your own class by overriding this parameter in your config.yml file:
# app/config/config.yml
parameters:
grid.class: your own class, for example AppBundle/Grid/Grid
If in the future you find that you need to override a class and it cannot be done through one of these methods, you can always create your own fork of the bundle, though this is a pain and hard to maintain, so I would only recommend it if you find no other alternative

Related

PHP: Is AutoLoader able to load multiple class in a single php file?

Quote from Autoloading Classes :
Many developers writing object-oriented applications create one PHP
source file per class definition. One of the biggest annoyances is
having to write a long list of needed includes at the beginning of
each script (one for each class).
In PHP 5, this is no longer necessary. The spl_autoload_register()
function registers any number of autoloaders, enabling for classes and
interfaces to be automatically loaded if they are currently not
defined. By registering autoloaders, PHP is given a last chance to
load the class or interface before it fails with an error.
Here comes the question, what if there are multiple classes in a single php file, is it suitable for autoload usage? or do I have to use require filepath statement?
For example, I have a protocol file under Protobuf\Client.php:
<?php
namespace Protobuf;
class A {
...
}
class B {
...
}
You would have to have some complex function to autoload those classes from the file named Client.php. The idea is to translate your namespace\classname into a directory\filename.php
In this instance you would need to name your file A.php then when you call new Protobuf\A() it will find it. Otherwise you will have to create an overly-complex autoloader.
Let's say you do create the autoloader so it finds the A class, then you can have B on the same file, but only if you have already autoloaded A otherwise you have to make some algorythm to know that A and B are on the same page.
I would do the above pattern or the pattern adopted by apps like Magento that turn class names into directory paths by replacing underscores:
$class = new Core_Classes_MyClass_Client();
Your autoloader would replace the underscores and will load:
Core/Classes/MyClass/Client.php //or similar scheme
This to me is an easy way to do it, but I prefer using namespace and class. The above method is not in favor at the moment and from a naming standpoint, very easy to get mixed up since a lot of classes may be in the same folder or nested really deep into sub folders. You could get some really long naming for classes.
To answer this question directly, we can use classmap for autoload in composer.json to support the single file containing multiple class.
For example we are going to support the single file Protobuf\Client.php, which has two class A and B in it:
<?php
namespace Protobuf;
class A {
}
class B {
}
We add classmap in the composer.json as following:
{
"name": "hailong/myproj",
"autoload": {
"classmap": [
"Protobuf/Client.php"
]
}
}
Then, in main.php we can use class A and B:
<?php
require __DIR__.'/vendor/autoload.php';
use Protobuf\A;
use Protobuf\B;
$a = new A();
$b = new B();
Finally, don't forget to run composer dump-autoload after changing the composer.json, which will magically generate the autoload.php that required in our main.php.
For people not quit familiar with the composer yet, here is the final files structure we will get:
myproj % tree
.
├── Protobuf
│   └── Client.php
├── composer.json
├── main.php
└── vendor
├── autoload.php
└── composer
├── ClassLoader.php
├── LICENSE
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
├── autoload_static.php
└── installed.json
3 directories, 12 files
To extend on Rasclatts very informative answer,
Ideally, it's always good practice to separate classes when it comes to autoloading. I strongly recommend looking into composers PSR-0 Namespace Autoloading
PSR-0 Allows you to beautifully organise all your classes into sub folders with infinite depth, take the following folder structure
\system
- Members
- Members.php
- Auth
- Auth.php
- Database
- Database.php
For this example, in each of the php files above you would have namespace MyNameSpace; before your class declaration and then in your composer.json you would have something similar to (documentation):
"autoload": {
"psr-0": { "MyNameSpace": "/system" }
}
Composer should be installed on your local/host computer for you to compile your autoload files, open terminal and navigate to your project directory and type:
composer dump-autoload -o
Now everything is neatly organised and you can access your classes similar to:
\MyNameSpace\Auth::staticFunction();
Yes And NO,
But what if I do generate (automaticaly) classes and methods for a very large WSDL ?
ie. for a more than hundred methods You would have probably hundred methodRequests (as a class object), next hundred methodResponse (as a Class Object) and large arrays ie ClassMap.
Sometimes it's better to handle that stuff in one file, especialy when developing without good docs for WSDL.

PHP - Namespace not found, autoload project with composer

I'm quite new to autoloading and namespaces, and attempting to add namespaces to my MVC project.
So In my composer I added:
"autoload": {
"psr-0": {
"prj\\app\\": "app/",
"prj\\app\\controller\\": "app/controller/",
"prj\\app\\classes\\": "app/classes/"
}
}
And then updated composer and ran the autodump command.
I then went back to my app to try use one of these namespaces however I just get the following back:
use \app\classes\engine; // use statement I tried
Fatal error: Uncaught Error: Class 'app\classes\engine' not found in C:\inetpub\wwwroot\web\index.php:87 Stack trace: #0 {main} thrown in C:\inetpub\wwwroot\web\index.php on line 87
I'm not sure why it is unable to find the class using the namespace, here's my entire folder structure if it may be of any use:
PRJ
├───app
│ ├───classes
│ └───controller
├───web
│ └───index.php
├───vendor
│ ├───bin
│ ├───composer
│ ├───...
└───view
├───bootstrap
└───default
/app stores the logic such as controllers and classes.
/web is the web root - the index.php is the page which visitors see and also everything is handled through here.
/vendor is the composer directory where my dependencies are stored.
There are several things going wrong. First of all, you're adding duplicate namespaces in your composer.json:
The lines:
"prj\\app\\controller\\": "app/controller/",
"prj\\app\\classes\\": "app/classes/"
Are unnecessary as they are already covered by:
"prj\\app\\": "app/",
As long as the directory under app matches the name of the namespace you use, there is no need to define it explicitly. So you can just add:
"autoload": {
"psr-0": {
"prj\\app\\": "app/"
}
}
Secondly, your use statement seems off, you're trying:
use \app\classes\engine;
The leading slash should not be neccessary here, if you are already in the same namespace. Additionally, you're autoloading your namespaces as prj\app and not app, so you're missing the prj bit. It should look something like this (when this is a file inside the app folder):
<?php
namespace prj\app\controller;
use prj\app\classes\engine;
class MyController
{
/**
* #var engine
*/
private $engine;
public function __construct()
{
// This should now work since engine should be autoloaded
$this->engine = new engine();
}
}
Also take a look at the PSR-0 naming conventions as you don't seem to follow them. Class names and namespace folders should be capitalized, like App\Classes\Engine instead of app\classes\engine.

Change laravel directory structure

atm i'm beginning to develop a big application and the current structure of laravel just doesn't fit what i had in thought. The controllers, models and views are all in seperate folders just bunched up and things could get messy when there's loads of them.
So I was hoping I could change the default way laravel loads it's controllers views and models. My approach would be something like this:
-App
--Content
---Login
----Controller
----Model
----View
----Js
---Home
----Controller
----Model
----View
----Js
--BaseContent
---BaseLayout
---BaseController
---BaseJS
So when the route is home the controller model view and javascript all get bundled in a folder instead of having all of it in one controller directory, model directory, view directory etc.
So my question is does someone know how I could change the way laravel load's it's dependency's? I'm kinda hoping for a configuration file I just can't seem to find.
I understand your thoughts. You want to group by feature.
It's possible. All you need are namespaces conform PSR-4. In composer.json you can link/specify the namespaces and paths, for example, you have in your controller this line:
namespace App\Http\Controllers;
In composer.json see the following fragment in autoload:
"psr-4": {
"App\\": "app/"
}
The namespace App refers to the directory app, and all classes with the namespace beginning with App\ will be autoloaded. So it's simple for your to change the directory structure.

How to load my own code into Laravel? Differences between those ways?

I'm kind of confused on how to load my own code (classes or just regular functions) into a Laravel app. I've seen this done in several ways:
Creating a folder inside the app directory (for example: app/libs) and add app_path().'/libs' to start/global.php
Add it into composer.json's "require"
Add "psr-0" into composer.json's "autoload" and add there the files
Add a My\Custom\Service\Provider into app/config/app.php's 'providers' and the alias for the facade
What's the difference between them? Why and when should I use any of those ways? Should I load a class, several .php files or simply the folder? Maybe reference those 3 things at the same time?
EDIT:
These are my guesses:
Option 2 is just for packages
Option 3 if you want to load every class inside a custom namespace declared within the new created folder (don't get why the "psr-0" instead of just adding it to "classmap")
Option 1 is the same as option 3, just handled by Laravel instead of Composer
You can reference a folder and it will load every class found inside, or you can reference a certain file and it will load the class found inside
About option 4:
If you want to use the facade anywhere on your code, and that will need the namespace added into composer.json
EDIT 2:
If you add them to "classmap":
"classmap": [
"app/libs"
]
every class from any namespace within files inside the app/libs folder, will be loaded
If you add them to "psr-0":
"psr-0": {
"Libs": "app/"
}
it will load every class within the Libs namespace inside the app/libs folder
Still not sure why/when to use service providers and aliases.
EDIT 3:
"psr-0" if I want to load a namespace that follows a folder structure (it won't load a class within a subnamespace if it doesn't match the folder structure)
"classmap" for "random" classes, functions... sort of "the rest"
you can load your own code 2 (maybe 3) ways in laravel.
use composer
use ClassLoader
Manual include or require anywhere
Option 2 is just for packages
yes, you're right.
Option 1 is the same as option 3, just handled by Laravel instead of
Composer
yes, you're right.
Option 3 if you want to load every class inside a custom namespace
declared within the new created folder (don't get why the "psr-0"
instead of just adding it to "classmap")
some packages or classes adhere psr-0 standard, the rest is not. psr-0 option is mapping namespace to directory. the classmap is mapping
the namespace to certain directory or file and used for the class that is not adhere psr-0 standard.
You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0/4.
If you want to use the facade anywhere on your code, and that will
need the namespace added into composer.json
nope, instead, you have to add class alias for the facade in app/config/app.php
if your code is just file, not a class, then use composer autoload files
if your code is class but not adhere psr-0/4 standard, use composer autoload classmap or just add the containing directory to app/start/global.php.
otherwise, use composer autoload psr-0 or psr-4.
in Laravel 4,I add all of my class in "mylibrary" folder.
Then at app/start/global.php , I add app_path().'/mylibrary',
ClassLoader::addDirectories(array(
app_path().'/commands',
app_path().'/controllers',
app_path().'/models',
app_path().'/database/seeds',
app_path().'/mylibrary',
));
Within mylibrary there is MyClass.php , within MyClass.php there is test_myfunction()
and at app/view/home.blade.php I add these code :
<?php
$FMyClass11 = new MyClass;
$just_test=($FMyClass1->test_myfunction());
?>
Hope it works for you. :)

PHP: Storing objects in a Symfony plug-in module's lib/ directory

I am building a Symfony project, and have created a new plug-in named sfUtilsPlugin. I currently have a directory structure that looks like this:
sfUtilsPlugin/
modules/
sfSearchLucene/
actions/
config/
lib/
templates/
Now, in the sfUtilsPlugin/modules/sfSearchLucene/lib directory, I have an object called sfLucene. The idea was that this object is accessible from the Symfony auto loading mechanism, so that it can be instantiated from anywhere within the application.
However, simply adding the sfLucene.class.php file to the sfUtilsPlugin/modules/sfSearchLucene/lib directory does not appear to add it to the autoloader.
Does anyone out there know why this might be happening? Perhaps it is just not possible to automatically use objects stored in this location inside Symfony.
Any advice is appreciated.
Because you are adding this class in lib subdirectory of module sfLucene, it will be autoloaded only if current module is sfLucene.
You have two options:
put this class somewhere into sfUtilsPlugin/lib directory;
require them every time you need it

Categories