I'm working with Magento, but this isn't a Magento specific question.
Let's say that you're working with foo.php with contains the class Foo. In Magento, /local/foo.php will be included if it exists, otherwise /core/foo.php will be included. In both, the class Foo is defined. The problem here is that both files contain the class Foo, therefore the class in /local/foo.php can't extend the class in /core/foo.php. Ultimately this requires all of the code from /core/foo.php to be copied in /local/foo.php minus my customizations.
/core/foo.php - I can't change this file!
<?php
class Foo {
public function test() {
echo 'core/foo.php :: Foo :: test';
}
}
?>
/local/foo_include.php
<?php
namespace mage {
require '../core/foo.php;
}
?>
/local/foo.php - I can't put a namespace in this file, as I have no control over the file instantiating this class.
<?php
require './foo_include.php';
use mage;
class Foo extends mage\Foo {
function __construct() {
var_dump('Un-namespaced Foo!');
}
}
$foo = new Foo();
$foo->test();
?>
The above doesn't work, saying that mage\Foo doesn't exist. (It does work if the core Foo class is defined inside foo_include instead of being brought in through an include.
Is there any way around this that I'm missing?
Normally when I see something like this, I feel like the approach isn't right and the problem needs to be examined at a higher view. That said, if this is a hack and you know it, this tweak to your hack will work. But again, I don't recommend either approach.
Change foo_include.php to this:
eval('namespace Mage {?>'.file_get_contents(__DIR__ . '/../core/foo.php').'}');
I'm unsure how Magento works, but with Symfony, the correct solution would be to override the dependency's class via parameters.
http://symfony.com/doc/current/cookbook/bundles/override.html
Is there something similar in Magento? Then you can override the class with your class, and extend the other. This still means they'd need to be in different namespaces, however the framework won't try to go to \Foo since you overrode it. Like such:
# original class for a dependency
dependency.class = \Foo
# overriden with new class
dependency.class = My\Foo
Then in your version of Foo:
class My\Foo extends \Foo {}
Related
When I create a class object dynamically I don't understand why it throws class not found. However, if I will remove namespace App\Controls and use App\Controls\one, it worked perfectly example code:
namespace App\Controls;
use App\Controls\one;
class one {
public static function test_one($className, $object) {
return $object((new $className));
}
}
class two {
public function language($lang) {
echo 'I love '.$lang;
}
}
one::test_one('two', function($table) {
$table->language('PHP');
});
from this structure I want to call class two depends on the value I input. I also include use App\Controls\two but still not working, but that's not the case because supposedly I will not add another use App\Controls\className just to call create another class object and it's not possible from PHP.
In PHP, namespaces are resolved when the code is compiled, not when it is run.
So, when you write this:
namespace MyNamespace;
use MyOthernamespace\SomeClass;
$foo = new Foo;
$bar = new SomeClass;
The compiler looks at the current namespace, and the class names you've "imported", and compiles the code as though you'd written this:
$foo = new MyNamespace\Foo;
$bar = new MyOthernamespace\SomeClass;
(Note that you don't need to use anything in the current namespace; that's assumed as the default prefix for everything.)
In your code, the compiler doesn't know that the 'two' is going to be used as a class name, so it doesn't change it; so when the dynamic code runs, it comes out literally as:
return $object((new two));
Which won't work - there isn't a class called two, only one called App\Controls\two.
The solution to this is the magic ::class syntax, which tells the compiler to expand something like it would for a class name, and then turn it into a string. It's important to know that despite the name, it doesn't actually care if the thing it's expanding is a class, or exists at all, it just expands it and assumes you know what you're doing with it.
So if you write this:
namespace App\Controls;
one::test_one(two::class, function($table) {
$table->language('PHP');
});
It will be expanded by the compiler to this:
App\Controls\one::test_one('App\Controls\two', function($table) {
$table->language('PHP');
});
Then when it gets into the dynamic code, it will be referencing the right class name, and run the code as though it was this:
return $object((new App\Controls\two));
I have an object with some protected fields and a method that uses them. The method doesn't do exactly what I need it to do, but I cannot change the original code since I am writing an add-on.
Is it somehow possible to extend the class and override the method so that I could call it on predefined objects of the original class? I thought about monkey patching but apparently it is not implemented in php.
You can override a method by extending the parent class, initiating the new class instead of the parent class and naming your method exactly the same as the parent method, that was the child method will be called and not the parent
Example:
class Foo {
function sayFoo() {
echo "Foo";
}
}
class Bar extends Foo {
function sayFoo() {
echo "Bar";
}
}
$foo = new Foo();
$bar = new Bar();
$foo->sayFoo() //Outputs: Foo
$bar->sayFoo() //Outputs: Bar
I hope below stategy will be works. asume that class is Foo and method is bar(). for override bar() method you have to make customFoo class as mentioned below.
class CustomFoo extends Foo{
public function bar(){
parent::bar();
}
}
I dont know actually what you need because you dont have explained in detail. Still I have tried my best. :)
Try creating a child class that extends the base or parent class that the object currently derives from.
Create a new method with exactly the same name as the method in the Parent class and put your logic in there.
Now instantiate your object from your new class, you would have succeeded in overriding that particular method and still have access to the methods and properties of the base class.
Problem is, once you've loaded the class, you can't officially unload it, and you do need to load it in order to extend it. So it's pretty tied up. Your best bet is to either hack the original class (not ideal) or copy paste the original class definition into a new file:
class ParentClass {
//Copy paste code and modify as you need to.
}
Somewhere after the bootstrapping of your framework:
spl_autoload_register(function ($class) {
if ($class == "ParentClass") { //Namespace is also included in the class name so adjust accordingly
include 'path/to/modified/ParentClass.php';
}
},true,true);
This is done to ensure your own modified class will be loaded before the original one.
This is extremely hacky so first check if the framework you're using has native support for doing this.
I'll try to describe the situation I'm having problems with:
I have one main folder. I keep all files in there (empty classes for a reason), and one sub-folder, containing the same files, with all implementations here (empty classes extend them).
the main folder's namespace is declared as Project/Folder, and the sub-folder as Project/Folder/Subfolder. These are class's declarations:
namespace Project\Folder;
class Foo extends Subfolder\Foo { }
namespace Project\Folder\Subfolder;
class Foo { }
What I want to achieve is to be able to call other classes from inside of the Project\Folder\Subfolder\Foo through these empty classes on the lower level, with only its name, e.g.:
namespace Project\Folder\Subfolder;
class Foo {
function bar() {
Another_Class::do_something();
}
}
By default, there will be called Another_Class from the Project\Folder\Subfolder namespace. I want this to refer to Another_Class from the Project\Folder namespace with the same syntax - is that possible?
I hope I explained this clear enough, if not, write a commend, and I'll try to make it clearer.
You can achieve that using the use statement.
use Project\Folder\Subfolder\Another_Class as SomeAlias;
// ...
SomeAlias::doSomething();
// or
$object = new SomeAlias();
$object->doSomething();
Alternatively, you would have to reference the entire namespace:
\Project\Folder\Subfolder\Another_Class::doSomething();
// or
$object = new \Project\Folder\Subfolder\Another_Class();
$object->doSomething();
More information here.
I read an article about namespaces in PHP. But I don't get what they are used for?
<?php
namespace MyProject {
// Regular PHP code goes here, anything goes!
function run()
{
echo 'Running from a namespace!';
}
}
I also read the PHP.net manual about it, but didn't quite get it.
I had a tough time as well, just think of it as a way to help the compiler resolve names.
So there is no ambiguity.
You could have two developers writing completely different classes but with same type identifier.
The class names could be the same. Grouping in namespaces helps the compiler/interpreter will remove the ambiguity.
So namespace Developer1.CoolClass is quite different from namespace Developer2.CoolClass
In the PHP world, namespaces are designed to solve two problems that authors of libraries and applications encounter when creating re-usable code elements such as classes or functions:
Name collisions between code you create, and internal PHP classes/functions/constants or third-party classes/functions/constants.
Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving readability of source code.
PHP Namespaces provide a way in which to group related classes, interfaces, functions and constants.
Check Here for details
Namespaces are a way to group your related classes in packages.You can assume namespaces as folders where you keep your files,in a way that both can have the files with same name but different (or same) without any ambiguity.
file1.php
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
file2.php
<?php
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
}
/* Unqualified name */
foo(); // resolves to function Foo\Bar\foo
foo::staticmethod(); // resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO
/* Qualified name */
subnamespace\foo(); // resolves to function Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // resolves to class Foo\Bar\subnamespace\foo,
// method staticmethod
echo subnamespace\FOO; // resolves to constant Foo\Bar\subnamespace\FOO
/* Fully qualified name */
\Foo\Bar\foo(); // resolves to function Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // resolves to class Foo\Bar\foo, method staticmethod
echo \Foo\Bar\FOO; // resolves to constant Foo\Bar\FOO
?>
Consider your write your own class, lets called it Foo. Someone else writes also a part of the project and he calls one of his classes also Foo.
Namespaces solve this problem.
Example:
Namespace MyClasses;
Class Foo
{
}
NameSpace HisClasses;
Class Foo
{
}
$myfoo = new MyClasses\Foo();
$hisfoo = new HisClasses\Foo();
Namespaces are used to isolate functions and class declarations in order to make libraries and "helpers" (files containing functions) more portable. By putting a library in a name space, you reduce the chances of your class names colliding with what an author who may want to use your library may want to call their classes For example, you can have multiple classes named "user" if they're in separate namespaces.
I ran into an issue with parent/child classes in different namespaces that I'm wondering if it's my improper use, or if this is a quirk of PHP namespaces:
<?php
namespace myVendorName;
class MyParentClass {
protected $args = array();
function factory($class) {
return new $class($this->args);
}
}
?>
<?php
namespace myVendorName\subPackage;
class foo {
}
class bar extends \myVendorName\MyParentClass {
function myFunc() {
var_dump(new foo()); // Works (we're in the same namespace)
var_dump($this->factory('foo')); // Doesn't work (no such class as myVendorName\foo)
var_dump($this->factory('subPackage\foo')); // Works
}
}
?>
The above code doesn't work as I'd expect. In the \myVendorName\subPackage\bar->myFunc() method, I'd like to get a \myVendorName\subPackage\foo class. If I just create it there, I can create it by only its class name since the namespace is the same. However, those classes in reality are more complex, and there's a factory method to create them. The factory method is universal, and is defined in the root vendor namespace. And the bar class doesn't overwrite that factory method; it simply inherits it. However, when referring to a class object by name, it seems to still operate in the parent's namespace, rather than having the method truly get inherited by the child and operate in the child's namespace.
Is there a way for a parent class method that's going to be inherited directly to use the child's namespace, or at least peek at what it is? Or does each of my child classes have to overwrite the factory method to fix the namespace, and call the parent method within themselves?
The answer, as Passerby indicated is that PHP does have a __NAMESPACE__ constant that is filled with the current namespace (though no leading slash). So modifying the factory function to:
function factory($class) {
if ($class[0] != '\\') {
$class = '\\'.__NAMESPACE__.$class; // If not a fully-qualified classname, prepend namespace
}
return new $class($args);
}
works as expected (there's an example of this in the PHP documentation)