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.
Related
I have a question about using Trait and Interfaces in PHP.
A trait with foobar function
<?php
trait FoobarTrait
{
protected $foobar;
public function setFoobar($foobar)
{
$this->foobar = $foobar
}
public function getFoobar()
{
return $this->foobar;
}
}
The specific Interface to specify how to use Trait
<?php
interface FoobarInterface
{
public function setFoobar($foobar);
public function getFoobar();
}
I want use foobar feature in a class. What is the best way ?
It is necessary to implements with an interface and specify trait or it is an induced behavior ?
<?php
class FoobarClass implements FoobarInterface
{
use FoobarTrait;
}
Or this
<?php
class FoobarClass
{
use FoobarTrait;
}
Thank's for your reply and debate ;)
As it was correctly stated in the comments by #Federkun, "it depends". In my opinion mostly on how are you about to use your FoobarClass.
If it's an implementation of some kind of service that can have multiple implementations depending on external conditions (consider file system or S3 for handling user uploads, as an example), I would use FooBarInterface as I can then type-hint it in other places using the service.
If you wish to just avoid repeating yourself, you could use a trait and no interfaces. Or even a base abstract class AbstractFooBar { ... } to encapsulate the repeating code.
If you only have one implementation for getting and setting $fooBar - just put it all in the same class :)
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.
Can anyone tell me if it's possible to override a use statement?
My example is having an MVC setup where there is core code with the ability to override each Controller / Model with a custom version that extends the core version.
The issue I face is that my core controller has a use statement telling it to use the core model, so if I extend the model, I'm not sure how to tell it to use the custom model rather than the core one
I could obviously update the core controller use statement to point to the custom one, but the core code is shared so the custom version may not exist on other sites that use this core core
Use statements are obviously file level so I'm guessing it's not possible, but I'm hoping there's either something I don't know about or maybe a workaround
Example
Core controller
namespace Core;
use Core\Model\Example as ExampleModel;
class ExampleController {
public function output() {
$model = new ExampleModel;
$model->test();
}
}
Core Model
namespace Core;
class ExampleModel() {
public function test() {
echo 'This is the core test';
}
}
Custom Controller
namespace Custom;
use Custom\Controller\Example as Base,
Custom\Model\Example as ExampleModel;
class ExampleController extends Base {
//Inherits the output() method
}
Custom Model
namespace Custom;
use Core\Model\Example as Base;
class ExampleModel extends Base {
public function test() {
echo 'This is the custom test';
}
}
So given this example, is it possible for me to create an instance of the custom controller which uses the custom model to output 'This is the custom test', without modifying the core code at all?
Hopefully what I'm asking makes sense
Thanks
I'm not quite sure I understand your question, but the answer should be self-evident: If your custom model extends from the core model, you can simply extend another class from that custom class
If you are writing code, that depends on a child of the core class being present, then that child class becomes a vital part of your project. If you can't change the core itself, add that class as a dependency. It's as simple as that.
Adding a second layer of inheritance needn't worry you, it's perfectly common to do so. Something like this is perfectly predictable, and reliable:
namespace Core;
class Model
{
public function coreTest()
{
return 'from the core';
}
}
namespace Custom;
use Core\Model;
class CustomModel extends Model
{
public function customTest()
{
return 'from the custom model';
}
}
//finally
namespace Project;
use Custom\CustomModel;
class ProjectModel extends CustomModel
{
public function test()
{
return array(
$this->coreTest(),
$this->customTest(),
'From the project'
);
}
}
$test = new ProjectModel();
echo implode(PHP_EOL, $test->test());
If, however you want a given class to extend from another class, based on whether or not that class exists, you are looking for conditional imports.
A simple use statement is evaluated at compile-time, so there's no way you can use an if check to switch between which class you extend from.
There is, however a hacky work-around, but I wouldn't rely on it. Check if the given class exists (without autoloading), and set an alias to the class that does.
if (!class_exists('\\Custom\\Model', false))
class_alias('\\Core\\Model', 'Base');
else
class_alias('\\Custom\\Model', 'Base');
class CustomModel extends Base
{}
But really: don't go down this route. Sure your code will work, but if you then rely on a method being available, that was defined in the custom class, but that class was missing, then your code will fail... horribly.
Details on conditional imports:
Why use class alisases?
I was just wondering if the next situation could be possible or not, I've read the PHP Manual documentation, but I would like another perspective because it's not so clear for me.
So I have for example one class:
class SomeClass {
public function someFunction() {
...
}
}
And an extension of it:
class Extension extends SomeClass {
public function someOtherFunction() {
...
}
}
My question is, could I be able to use the public functions inside the classes on both ways, the main class's function inside the extended function and the other way around?
And would I be doing that how?
You can use both functions from class Extension, but only someFunction() from class SomeClass.
Extension does not change the original class, it just incorporates it into a new one.
You can use the public and protectedfunctions of your parent in the extended (child) class:
class Extension extends SomeClass
{
public function someOtherFunction() {
$foo = $this->someFunction(); // from parent class
return $foo;
}
}
When class "Extension" is created, its basically a copy of "SomeClass" which you can modify in the way as you can add new functions or overwrite those of the parent class.
The parent does not know about the Extension (it can be extended multiple times, eg "JSONRequest extends Request", "XMLRequest extends Request"). Calling extended functions from within the parent makes no sense, since the parent class can never know which childs function it should call in such a situation. This type of Inheritance is one of the basic concepts of OOP and clear interfaces.
In other words, no it will never work the other way round. And it should not.
I was wondering what should I do with my entities? For example, a class named Articles, with a few datamembers (name/title/date) and getters & setters. I could add these to my Articles datamember, but it's better practice to seperate those. So what do you think?
Thanks!
i usually do this:
1.- create my entity classes in /system/application/classes
class MyEntity {
}
2.- define a constant to point to that folder on /system/application/config/constants.php
define('CLASSES_DIR', APPPATH . "classes/");
3.- include the entities classes from the models:
require_once(CLASSES_DIR . "MyEntity.php");
class MyModel extends Model {
function test() {
$entity = new MyEntity();
$entity->doSomeStuff();
}
}
That won't break your mvc structure, and keeps for entities classes separated. Hope that helps!
I liked #ilbesculpi's solution, but I customized it a bit using a namespace rather than using a constant and require_once. Here is my version:
1) Create my entity classes in /system/application/entities.
namespace MyApplication\Entities;
class ArticlesEntity
{
function doSomeStuff()
{
// Your code here...
}
}
2) Include my entity class in my model via a using statement.
use MyApplication\Entities\ArticlesEntity;
class ArticlesModel extends CI_Model
{
function test()
{
$entity = new ArticlesEntity();
$ArticlesEntity->doSomeStuff();
}
}
CodeIgniter models use the singleton pattern. You can create libraries or use $foo = new Some_Model if you like, remember that its all just PHP :)