Is it possible to declare a class and have it extend a variable?
class Child extends $parentClass {}
Yes, it is with eval. But it is not recommended.
<?php
function dynamic_class_name() {
if(time() % 60)
return "Class_A";
if(time() % 60 == 0)
return "Class_B";
}
eval(
"class MyRealClass extends " . dynamic_class_name() . " {" .
# some code string here, possibly read from a file
. "}"
);
?>
Is Eval an evil?! Read this.
no you cant.
this is because in a normal definition:
class A extends SomeOtherClass {
}
SomeOtherClass is a namespace reference, not an instance of the class. And you cant use a variable to provide that namespace because variables in PHP are defined at runtime, whereas classes are defined in the compile phase.
But... if youre using PHP7, you can do this:
$newClass = new class extends SomeOtherClass {};
which is not exactly what you want, but goes some way.
A Class can not extend from a variable , You need to wrap the variable inside a class to extend . Hope this helps
I know this is an old question, but I hope my answer helps someone.
You can use class_alias() to an "intermediate" class:
class_alias($parentClass, 'ParentClassAlias');
class Child extends ParentClassAlias {}
Related
I used to have several library classes with the exact same methods. Figured I'd learn a bit more about two important aspects of coding; Traits and DRY.
I have the following trait:
<?php
namespace App\Berry;
trait Lib
{
public function getIDs()
{
$oClass = new \ReflectionClass(get_called_class());
$aConstants = $oClass->getConstants();
foreach($aConstants as $sKey => $mValue)
{
if(!is_int($mValue))
{
unset($aConstants[$sKey]);
}
}
return array_values($aConstants);
}
}
The following class:
namespace App\Berry;
use Lib;
class postType
{
const POST_TYPE_BLOG_ID = 1;
const POST_TYPE_BLOG_LABEL = __('blog', 'lib');
const POST_TYPE_PAGE_ID = 2;
const POST_TYPE_PAGE_LABEL = __('page', 'lib');
const POST_TYPE_NEWS_ID = 3;
const POST_TYPE_NEWS_LABEL = __('news', 'lib');
}
And am calling it like this in my PicturesController class:
$cPostTypesLibrary = new postType();
$this->set('aPostTypes', $cPostTypesLibrary->getIDs());
Now to me, this seems almost exactly like the tell me to do in the docs example #4 (About using multiple traits)
The only difference I have is I have the use outside of my class due to getting cannot use class because it is not a trait
What am I missing here?
Your class is not using the trait, you are instead using the other use of the use keyword and trying to import the Lib class from the same namespace into, well, the same namespace.
To use traits correctly, go back to the documentation, and look at where they are placed. The use statement is placed inside of the class definition. In your case, it would look like this:
namespace App\Berry;
class postType
{
use Lib;
// ...
}
You have to declare the trait inside the class
class postType
{
use Lib;
}
I'm creating a factory class in my project, where this class gets a Report Type as a string. This string has the name of the concrete class that implements a Report Interface.
The issue I'm having is that when I'm instantiating this class, I get a Class not found error.
Here follows the factory code.
namespace App\Term\Reports;
class Factory
{
public static function build($type)
{
$obj = new CableBySensor(); // Works!
// $type == 'CableBySensor'
$obj2 = new $type; // Class not found :(
// ... validates if the class exists ...
// ... and if it implements the Report Interface ...
// ... throw exception if class doesn't exist or doesn't implements interface
// ... then returns the corresponding object.
}
}
Both methods are virtually the same thing.
First: Why do I have to specify the full qualified name of the class in the string to make it work? The class CableBySensor resides in the same namespace as Factory.
This started giving me trouble because I also want to validate that the class being instantiated implements a ReportsInterface.
Second: How do I overcome this? Should I call the factory like this $myReport = Factory::build('App\Term\Reports\' . $className); or should I use the __NAMESPACE__ constant inside the Factory class such as this: $obj = new __NAMESPACE__ . '\' . $className?
Thank you.
Indifferently whether the factory approach is useful here or not,
the problem is with trying to instantiate from a dynamic variable.
Or as akhoondi at php.net pointed out:
One must note that when using a dynamic class name [...] the "current namespace" [...] is global namespace.
There are possibly 3 solutions:
pass the fully qualified class name to your factory method (arghh...)
$instance = Factory::build('Acme\CableBySensor');
Or, do a check in your build method and prefix the namespace if necessary (as suggested here) (sounds not so fool proof to me)
public static function build($type)
{
if ($type[0] !== '\\') {
$type = '\\' . __NAMESPACE__ . '\\' . $type;
}
$obj = new $type;
...
}
Or, if you have PHP 5.5+ why not use class name resolution via ::class?
Personally, I would go for that one whenever possible:
$instance = Factory::build(CableBySensor::class);
In my php file i am doing it this way
pagecontroller.php
include_once(RUDRA."/controller/AbstractTemplateController.php");
if (file_exists(get_include_path() . CONTROLLER_PATH . "/TemplateController.php" )) {
include_once (CONTROLLER_PATH . "/TemplateController.php");
} else {
include_once (RUDRA . "/controller/TemplateController.php");
}
in TemplateController.php a class named 'TemplateController extends AbstractTemplateController' is defined, if a developer has already defined a class TemplateController which also extends AbstractTemplateController then it will use that otherwise it will fallback to default definition.
then in other files i will simply use something like this
include_once("pagecontroller.php")
$c = new TemplateController();
is there any better way to do this?
since I am including two files AbstractTemplateController.php & TemplateController.php in both cases, I cpuld have written both class definitions in same file which would have saved one include(if there is no custom TemplateController.php)?
I tried writing AbstractTemplateController & TemplateController in one single file but if then developer has defined his own TemplateController it creates two classes with same name situation.
pupose is to have atleast one definition to be there, if customDefinition does not exists then only use default one. and this code is to be abstract.
in the beginning if CustomClass exists (in a specific folder) then that the definition to be used, else use default one (which is nothing but simply extends AbstractOne)
CONTROLLER_PATH . "/TemplateController.php"
class TemplateController extends AbstractTemplateController {
/* over-ridden method of AbstractTemplateController
*/
public function invoke($abc,$def){
echo $abc . " " .$def;
}
}
RUDRA . "/controller/TemplateController.php"
class TemplateController extends AbstractTemplateController {
// nothing at all this is simply to make sure TemplateController class is available
// for others to use.
}
Use namespaces and convention.
E.g. you could check if there's a TemplateController-class present that extends the AbstractTemplateController that's different from your namespace (As your implementation will be specific for your namespace), if there isn't ; fall back to your implementation of the TemplateController.
http://php.net/manual/en/language.namespaces.php
php provides a function for not letting you load/write a class more then once.
bool class_exists ( string $class_name );
example is :
<?php
function __autoload($class)
{
include($crigger_error("Unable to load class: $class", E_USER_WARNING);
}
}
if (class_exists('MyClass')) {
$myclass = new MyClass();
}lass . '.php');
// Check to see whether the include declared the class
if (!class_exists($class, false)) {
trigger_error("Unable to load class: $class", E_USER_WARNING);
}
}
if (class_exists('MyClass')) {
$myclass = new MyClass();
}
?>
in above example autoload is used, you could do it without autoload this way :
<?php
// Check that the class exists before trying to use it
if (class_exists('MyClass')) {
$myclass = new MyClass();
}
?>
still i am saying you better get habit of using namespaces. they are awesome and work every where.
How can I retrieve a class namespace automatically?
The magic var __NAMESPACE__ is unreliable since in subclasses it's not correctly defined.
Example:
class Foo\bar\A -> __NAMESPACE__ === Foo\bar
class Ping\pong\B extends Foo\bar\A -> __NAMESPACE__ === Foo\bar (it should be Ping\pong)
ps: I noticed the same wrong behavior using __CLASS__, but I solved using get_called_class()... is there something like get_called_class_namespace()? How can I implement such function?
UPDATE:
I think the solution is in my own question, since I realized get_called_class() returns the fully qualified class name and thus I can extract the namespace from it :D
...Anyway if there is a more effective approach let me know ;)
The namespace of class Foo\Bar\A is Foo\Bar, so the __NAMESPACE__ is working very well. What you are looking for is probably namespaced classname that you could easily get by joining echo __NAMESPACE__ . '\\' . __CLASS__;.
Consider next example:
namespace Foo\Bar\FooBar;
use Ping\Pong\HongKong;
class A extends HongKong\B {
function __construct() {
echo __NAMESPACE__;
}
}
new A;
Will print out Foo\Bar\FooBar which is very correct...
And even if you then do
namespace Ping\Pong\HongKong;
use Foo\Bar\FooBar;
class B extends FooBar\A {
function __construct() {
new A;
}
}
it will echo Foo\Bar\FooBar, which again is very correct...
EDIT: If you need to get the namespace of the nested class within the main that is nesting it, simply use:
namespace Ping\Pong\HongKong;
use Foo\Bar\FooBar;
class B extends FooBar\A {
function __construct() {
$a = new A;
echo $a_ns = substr(get_class($a), 0, strrpos(get_class($a), '\\'));
}
}
In PHP 5.5, ::class is available which makes things 10X easier. E.g.
A::class
Use Reflection class.
$class_name = get_class($this);
$reflection_class = new \ReflectionClass($class_name);
$namespace = $reflection_class->getNamespaceName();
I posted some questions previously regarding the use of Namespaces in PHP and from what I got, this example code I have below should be working.
However I am getting errors when I try to use Namespace in PHP like this. Here is the first error when running the code below as is...
Fatal error: Class 'Controller' not found in E:\Controllers\testing.php on line 6
E:\Controller\testing.php File
<?php
use \Controller;
include('testcontroller.php');
$controller = new Controller;
$controller->show();
?>
E:\Controller\testcontroller.php File
<?php
use \Library\Registry;
namespace Controller
{
class Controller
{
public $registry;
function __construct()
{
include('E:\Library\Registry.class.php');
$this->registry = new Registry;
}
function show()
{
echo $this->registry;
echo '<br>Registry was ran inside testcontroller.php<br>';
}
}
}
?>
E:\Library\Registry.class.php File
<?php
namespace Library\Registry
{
class Registry
{
function __construct()
{
return 'Registry.class.php Constructor was ran';
}
}
}
?>
As you can see I tried to make it as simple as possible just to get the Namespace part working. I have tried different variations and cannot seem to figure it out.
Even when using use statement, you need to specify the namespace of the class you are trying to instantiate. There are a lot of examples here: http://www.php.net/manual/en/language.namespaces.importing.php
To understand it better, I will describe to you how it works. In your case, when you do use \Controller, the whole Controller namespace becomes available to you, but not the classes that are in this namespace. So, for example:
<?php
include('testcontroller.php');
use \Controller;
// Desired class is in namespace!
$controller = new Controller\Controller();
// Error, because in current scope there is no such class
$controller = new Controller();
$controller->show();
?>
Another example:
testcontoller.php:
<?php
namespace Some\Path\To\Controller;
class Controller
{
function __construct()
{
}
function show()
{
echo '<br>Was run inside testcontroller.php<br>';
}
}
?>
testing.php:
<?php
include('testcontroller.php');
use \Some\Path\To\Controller;
// We now can access Controller using only Controller namespace,
// not Some\Path\To\Controller
$controller = new Controller\Controller();
// Error, because, again, in current scope there is no such class
$controller = new Controller();
$controller->show();
?>
If you wish to import exactly the Controller class, you need to do use Controller\Controller - then this class will be accessible in your current scope.
Its not that good idea to name the namespace, like the class, because it is confusing (and I think this is what happens here). There moment you define the alias via use Controller this referenes to either a class \Controller, or the namespace \Controller, but your class, because it is within the namespace, is named \Controller\Controller 1
use Controller;
$class = new Controller\Controller;
or
$class = new \Controller\Controller;
or
use Controller\Controller;
$class = new Controller;
The idea is, that the moment you try to access a class with its relative name it tries to map the "first part" against any alias defined using use (remeber use MyClass is the same as use MyClass as MyClass. The thing after as is the alias).
namespace MyNamespace\MyPackage\SomeComponent\And\So\On {
class MyClass {}
}
namespace Another {
use MyNamespace\MyPackage\SomeComponent; // as SomeComponent
$class = new SomeComponent\An\So\On\MyClass;
}
As you can see PHP finds SomeComponent as the first part and maps it against the SomeComponent-alias the line above.
You can read more about it in the manual about namespaces.
1 Its called "Full-qualified classname", if you name a class with its complete name.
When you put a class Controller in the namespace Controller, then you have to reference it that way:
$controller = new Controller\Controller();
\Controller would be a class in the global (default) namespace, i.e. as if you used no namespace at all.
Strangely I have found that in my example code from the Question above, if I change all the Namespace's that are defined to something like MyLibrary so it would be like this code below...
E:\Library\Registry.class.php File
<?php
namespace MyLibrary
{
class Registry
{
function __construct()
{
echo 'Registry.class.php Constructor was ran';
}
}
}
?>
Then when I use use MyLibrary\Registry; in another file, I am able to access it how I had planned...
$this->registry = new Registry;
The reason this is very strange to me is this now makes a class name appear to be a Namespace as well. So I would not need to set a Namespace to 'MyLibrary\Library' to access the Registry instead I would do it like I showed in this answer to be able to access it with just calling the name of the class.
I hope this makes sense and helps someone else. I will not accept this as the answer as I am hoping someone with more know-how will come in and post a better Answer with explanation
try
<?php
use \Library\Registry;
namespace Controller;
class Controller
{
public $registry;
function __construct()
{
include('E:\Library\Registry.class.php');
$this->registry = new Registry;
}
function show()
{
echo $this->registry;
echo '<br>Registry was ran inside testcontroller.php<br>';
}
}
?>
and
<?php
namespace Library\Registry;
class Registry
{
function __construct()
{
return 'Registry.class.php Constructor was ran';
}
}
?>
First off, I believe you are using composer or composer is initialised in your project. If so, check composer.json file for your autoload, psr-4 definition. For example, if the root of your application is "App", then in your psr-4, you should be doing "autoload": { "psr-4": { "App\\": "./" } },
Furthermore, remember to clear composer cache and dump-autoload from the terminal as follows:
composer clear-cache
composer dump-autoload