Apologies if this is a duplicate, I did search for the answer previously.
I'm struggling to overload a method defined in a trait. It throws a fatal error:
Fatal error: Configuration has colliding constructor definitions coming from traits in Configuration.php on line 18
Their Class
<?php
namespace Theirs\Package;
use Theirs\TheirTrait;
class Configration
{
use TheirTrait;
}
My Class
<?php
namespace My\Package;
use Theirs\Package\Configuration as BaseConfiguration;
use My\Trait\MyTrait;
class Configuration extends BaseConfiguration
{
use MyTrait;
}
My Trait
use Theirs\TheirTrait as BaseSomeTrait;
trait MyTrait
{
use BaseSomeTrait;
protected function someMethod($something)
{
// ...
}
}
you can resolve the constructor collision like this.
trait MyTrait {
use BaseSomeTrait{
BaseSomeTrait::__construct as private __otherConstruct;
}
public function __construct(/* maybe params here*/)
{
// maybe other code
$this->__otherConstruct(/* maybe params here*/);
// maybe other code
}
}
if MyClass has a constructor as well you need to do it there additionally, or maybe only there if MyTrait has no constructor...
It looks like both Theirs\TheirTrait\Configration and Theirs\Package\Configuration have a constructor, and the constructor signatures are incompatible. (But without the code of both, it’s hard to tell.)
If Theirs\Package\Configuration is really intended to be used with Theirs\TheirTrait\Configration, you should write them a bug report. Otherwise, I guess there should be some documentation on how to use the class and the trait in your code so that they don’t produce errors.
Related
I have an abstract question for you.
Question:
How can a subclass that extends an abstract class register itself to the abstract class or another class?
Problem:
Guess we have a module master named ModuleMaster and maybe someone else writes another modules to handle a specific problem without modifying the master class and named it ModuleA. For that reason we want to implement a dynamic loading of problem solutions.
My idea:
File: Extensions.php:
namespace Project\Extensions;
class Extensions
{
public function getLoadedModules()
{
var_dump(ModuleMaster::LOADED_MODULES);
}
}
File: Modules\ModuleMaster.php:
namespace Project\Extensions\Modules;
abtract class ModuleMaster
{
public const LOADED_MODULES = array();
}
File: Modules\ModuleA.php:
namespace Project\Extensions\Modules;
class ModuleA extends ModuleMaster
{
}
I hope you understand what I mean and can help with that abstract problem.
This is very strange to use. It's probably a better design to have an external registry for your module. But I think you're asking for this:
File: Extensions.php:
namespace Project\Extensions;
use \Project\Extensions\Modules\ModuleMaster;
class Extensions
{
public function getLoadedModules()
{
var_dump(ModuleMaster::getLoadedModules());
}
}
File: Modules\ModuleMaster.php:
namespace Project\Extensions\Modules;
abstract class ModuleMaster
{
public static function getLoadedModules() {
$parent = self::class;
return array_values(array_filter(\get_declared_classes(), function ($class) use ($parent) {
return in_array($parent, class_parents($class));
}));
}
}
File: Modules\ModuleA.php:
namespace Project\Extensions\Modules;
use \Project\Extensions\Modules\ModuleMaster;
class ModuleA extends ModuleMaster
{
}
Example use:
$e = new \Project\Extensions\Extensions;
$e->getLoadedModules();
Example result:
array(1) {
[0]=>
string(34) "Project\Extensions\Modules\ModuleA"
}
Please note that the code only works if all your class files are included into the context before running getLoadedModules(). PHP won't know your class exists if it is not already loaded into the context.
You seem to be trying to create a capability in the parent class which is not required or inappropriate in the child class. This is the opposite of inheritance and hence an anti-pattern. Further, even though it might be considered as an extension of reflection, you are trying to put runtime data in a class - that's not what classes are for.
You've also not explained in any way that I can understand why you want to do this.
I suspect you really want to implement a factory, strategy or a registry object.
I am trying the following :
//file1.
namespace foo;
class mine {
public function mine() {
echo "Does not work!!";
}
}
//file2.
use foo/mine;
include "foo/mine.php";
$obj = new mine();
the above scenario is not working. No errors including the file -- constructor does not get called.
However when i use __constructor(), everything works fine. I am using php v5.4
From php manual:
For backwards compatibility, if PHP 5 cannot find a __construct()
function for a given class, and the class did not inherit one from a
parent class, it will search for the old-style constructor function,
by the name of the class. Effectively, it means that the only case
that would have compatibility issues is if the class had a method
named __construct() which was used for different semantics.
As of PHP 5.3.3, methods with the same name as the last element of a
namespaced class name will no longer be treated as constructor. This
change doesn't affect non-namespaced classes.
You can use the name of the class as constructor (unless the class is namespaced) because PHP5 keeps this for backwards compatibility with PHP4, but this is not recomended because it is the old way and may be removed in newer versions of php. So unless you are writting something that needs for some reason to be PHP4 compatible use __construct().
Below are 2 different possible solutions to the namespace\constructor problem
//parentclass.php
class parentclass
{
public function __construct()
{
//by default, strip the namespace from class name
//then attempt to call the constructor
call_user_func_array([$this,end(explode("\\",get_class($this)))],func_get_args());
}
}
//foo/bar.php
namespace foo;
class bar extends \parentclass
{
public function bar($qaz,$wsx)
{
//...
}
}
$abc = new foo\bar(1,2);
and
//parentclass.php
class parentclass
{
public function __construct()
{
//by default, replace the namespace separator (\) with an underscore (_)
//then attempt to call the constructor
call_user_func_array([$this,preg_replace("/\\/","_",get_class($this))],func_get_args());
}
}
//foo/bar.php
namespace foo;
class bar extends \parentclass
{
public function foo_bar($qaz,$wsx)
{
//...
}
}
$abc = new foo\bar(1,2);
This is a follow-up to my previous question about resolving the diamond issue in php.
As I state in that question, I resolve my problem by using traits and passing the instance of the class to the method of the trait. Such as:
trait SecurityTrait
{
public function beforeExecuteRouteTrait($controller, Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
return $this->beforeExecuteRouteTrait($this, $dispatcher);
}
}
However, I am still uncomfortable with this as I don't think this is how traits are really supposed to be used. In my reading I haven't found any way in which to access class members in traits (make $this inside a trait refer to the class using it). Is this possible? Or is there another way to implement a similar behaviour?
After reading some of the answers...
Previously I thought I had received errors when using $this->... inside the trait and this led me to believe the trait could not access anything to do with the underlying class. After reading the answers I tried altering my code to use $this->... inside a trait again and it works - which means a typo several weeks ago has given me far too much headache...
The example given previously now looks like this
trait SecurityTrait
{
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
}
Much cleaner and more easily understandable but provides the same functionality.
If you use a trait inside a class then that trait has full access to all class's members and vice versa - you can call private trait methods from the class itself.
Think of traits as code that literally gets copy/pasted into the class body.
For example:
trait Helper
{
public function getName()
{
return $this->name;
}
private function getClassName()
{
return get_class($this);
}
}
class Example
{
use Helper;
private $name = 'example';
public function callPrivateMethod()
{
// call a private method on a trait
return $this->getClassName();
}
}
$e = new Example();
print $e->getName(); // results in "example"
print $e->callPrivateMethod(); // results in "Example"
In my view referencing classes in traits is not the best way to use them but there's nothing stopping anyone from doing it.
No, that's exactly what Traits are for. Your class already extends a class so you can't inherit the methods and variables of any other classes.
Think of a Trait like copy/paste for code execution. When a class includes a Trait, it's just as if you had written all that code into the class itself.
Have encountered an issue I can't seem to figure out now by myself.
Using Symfony autoload module.
Here's my factory:
namespace Core\Factories;
use \Core\Gateway;
class DatabaseAccessFactory {
// Define client type
const DEF = 'mongo';
public function createObject($type) {
switch($type) {
case self::DEF:
return new MongoClientGateway();
break;
default:
return false;
}
}
}
Example of /Core/Gateway/MongoClientGateway.php
<? namespace Core\Gateway;
class MongoClientGateway implements MongoDbGateway {
public function setUp(){
}
public function query(){
}
public function save(){
}
}
So, basically I'm using "use" keyword to load gateway namespace into my current one, and then I try to instantiate a class that is under \Core\Gateway namespace, but it says class is not found. Am I missing something?
You need to specifcy the class as well
use Core\Gateway\MongoClientGateway
or access the class with the namespace you used
new Gateway\MongoClientGateway
Btw, there's no need for the first "\" in use \Core\Gateway
It's use Foo\Bar, without leading backslash.
use Foo\Bar does not mean that every Class implicitly resolves to Foo\Bar\Class now. use Foo\Bar is shorthand for use Foo\Bar as Bar, so you can reference the namespace Foo\Bar using merely Bar. use is not "importing a namespace", it's aliasing a namespace to a shorter name.
Therefore you need to write Gateway\MongoClientGateway, or use Core\Gateway\MongoClientGateway explicitly if you want to be able to write just MongoClientGateway.
you used "use" wrong.
waht "use" does, is to tell your code where class comes from.
sample code:
use \my\namespace\className
new ClassName();
this will make the className accassible without a namespace.
My current code is as following:
namespace Libraries;
class_alias('Libraries\ORM', 'ORM');
class ORM
{
public function __construct() {}
static public function someMethod()
{
// do something
}
}
I thought I could shortcut the namespace as you can see above, so I only needed to call the ORM::someMethod(); instead of \Libraries\ORM::someMethod();
(I am using the ORM class in another namespace, lets says 'Project')
Is this possible or what is the right solution?
I know that I could store the class in a global namespace, but then I still need to use the global slash like: \ORM::someMethod();.
Thanks!
Simply alias the classname when you are importing it:
namespace SomethingEntirelyDifferent;
use Libraries\ORM as ORM;
ORM::someMethod();