Extending classes in PHP - php

When extending classes in Java, class name ambiguity is avoided by the usage of qualified package names in the import statements.
For example: Say I want my controller to extend Spring's MultiActionController - I'll import the same from the standard Spring package. This also prevents me from extending Mike's or Perry's MultiActionController, since I have not imported MultiActionController from their packages.
Similarly in PHP, say we have 10 classes in 10 different library folders, all of which are called MultiActionController.
When I write:
class MyController extends MultiActionController {
function __construct() {
parent::__construct();
}
}
How do I tell PHP which MultiActionController (from which folder), to extend from?

Having several classes with the same name will, one day or another, cause some problems : you cannot include two classes with the same name during the execution of one script -- It'll get you a Fatal Error.
What's generally done, in PHP (before PHP 5.3 and namespaces, at least) is to include the name of the library and/or the "package" in the class name.
For instance, you could have a class name MyLibrary_Package_MultiActionController, and another called OtherLib_Blah_MultiActionController.
Then, what's generally done is using that class name to "map" it to directories and files, replacing the '_' by '/', and adding .php at the end of the last level -- this being often done using the autoloading feature of PHP, to avoid having to write an enormous amount of require directives.
For instance, a class named MyLibrary_Package_MultiActionController should be stored in MyLibrary/Package/MultiActionController.php.
As a sidenote : you used the tag "php4", in your question... If you are actually using PHP 4, you should not forget that it's old, not maintained anymore (Not even for security-related problems), and that PHP 5 is really the way to go !
In fact, you won't be able to do much about object-oriented programming, with PHP 4 ; the object-oriented stuff in PHP 4 was really basic...
(Stuff such as autoloading, which I wrote about a couple of paragraph earlier didn't exists in PHP 4 -- same for public/private/protected, and lots of other OO-related things...)

It depends which one you include. PHP will not let you redefine a class of the same name, so just include above the class definition (change to fit the file names and your software layout):
include('../includes/Spring/MultiActionController.php');
class MyController extends MultiActionController {
....
}

PHP will extend the class that you included with an include statement.
For example, say that you have a class foo declared in file bar.php:
class Foo {
// methods and fields
}
Then in another fie:
include 'bar.php';
class Aardvark extends Foo {
// this class will extend the class Foo in file bar.php
}

You have to include the file holding the class, with a banal include() statement:
include('lib/controllers/MultiAction.php');
Then you can extend it!

I would use namaspaces
namespace package_products_multicontroller {
include("/packages/products/multicontroller.php");
}
class MyController extends package_products_multicontroller\MultiActionController {
function __construct() {
parent::__construct();
}
}

Related

Overwriting class via composer

I'm using a composer Library, with lots of classes. There is one class (Alibrary\FileA) in this which doesn't do exactly what I want it to do.
namespace Alibrary;
class FileA
{
public function sayHello()
{
echo 'hello';
}
}
So I've written a replacement called Mylibrary\FileB. As you can see it's so must better.
namespace MyLibrary;
use \Alibrary\FileA;
class FileB extends FileA
{
public function sayHello()
{
echo 'hi';
}
}
Is there any way to tell Composer to load FileB every time FileA is asked for? I just want to replace one class, basically for testing purposes. I don't want to create a whole new repo - I've looked at https://getcomposer.org/doc/04-schema.md#replace already.
Is there something like this that I can do?
"classmap": [
"\Alibrary\FileA": "MyLibrary\FileB"
],
Thanks.
Your question has not very much to do with Composer in particular, but with how PHP works in general - and the code you are using.
Autoloading gets the name of the class that is still unknown to PHP and has to find the code that defines this class. But the situation you are facing would be the same if all classes are in one single file: The code is using class \Alibrary\FileA, and is not using class \MyLibrary\FileB. Note that your invented class names do suggest you are thinking in files, which is not the primary effect. Yes, classes' code is usually stored in files, but as soon as the code is loaded by PHP, it is only "the class" that is relevant, not where the code came from.
So you have some classes that do something, and one of them is \Alibrary\FileA. How can you change this to your own class? It depends. If the code creates that FileA class itself, you cannot override it from outside. If the code wants you to pass an instance of FileA as a parameter, you can instead pass your FileB class that inherits from FileA and must implement all that methods with the same parameter signature, and must return the same types. If it returns something completely different, it may work, but is likely to break.
Note that inheriting another class is in itself likely to break if you update the original library.

php namespaces: what needs to be qualified?

I am new to using namespaces in PHP and they seem very simple. However, when I added a namespace to the top of a large file containing classes, interfaces, and closures, the code stopped working altogether. Apparently some elements are not qualified properly.
For example:
<?php
namespace MyNamespace;
interface MyInterface { ... }
class MyClass implements MyInterface { ... }
...
I read the documentation at php.net, but I couldn't find a simple list of elements that require qualification under a single named space (either globally or otherwise).
So my question is, if you simply declare a namespace at the top of an otherwise namespace-free file, what elements would require qualification in that scenario?
The way you have it now with everything in one file like that, every class, interface or function in that file is now in the MyNamespace namespace. So from within those classes, interfaces and functions, if you want to refer to a class that is in a different namespace (global or named) you must use it's fully qualified name (or use use statements to declare aliases at top of the file). Note: pre-pending a \ will get you to the global namespace.
Additionally, from outside of the file, if you want to access one of those classes, interfaces or functions you must use the fully qualified name.
MyNamespace\MyInterface
MyNamespace\MyClass
I should also mention, this is not a typical set up. According to psr standards, you should have only 1 class per file. Take a look at http://www.php-fig.org/ for more guidance on standards and practices.

How to split a PHP class to separate files?

I want to do something like class categories in Objective-C, to define a class in one file, implement some core methods there, then implement some helper methods in another without subclassing or interfaces, just "continue" the class.
Possible in PHP?
As of PHP 5.4, you can accomplish this with Traits. To quote the official documentation:
Traits [enable] a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. [...] A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.
Using a trait allows you to store helper methods that address cross-cutting concerns in a central place and use these methods in whatever classes you need.
// File "HelperMethodTrait.php"
trait HelperMethodTrait
{
public function myIncredibleUsefulHelperFunction()
{
return 42;
}
}
// File "MyClass.php"
class MyClass
{
use HelperMethodTrait;
public function myDomainSpecificFunction()
{
return "foobar";
}
}
$instance = new MyClass();
$instance->myIncredibleUsefulHelperFunction(); // 42
a-priori PHP doest not give you this feature by language construction, the whole class must be in a single file.
Now there are tricks that could do the stuff, but they are quite heavy CPU demanding:
You can for example dissociate your classes between different files, then load the files and build a single string with the whole code, then do an eval(string) to interprete the classes and build them into the runnable code area.
not really a good idea btw for many reasons
If your only goal is to make the class definition file smaller, you can include files within a function definition
class MyClass {
public function doStuff($foo,$bar) {
global $quz;
include('MyClass.doStuff.php');
}
}
Within the include, your local scope applies ($foo,$bar and $quz will be defined)
I'm not sure what the overhead is - the include seems to happen at run-time, not compile time (since an include can actually return a value ..)
I think you better split your class in n parts along functional criteria, and then use the dependency injection pattern to "inject" class A into class B in the constructor for B, without a need for subclassing/interfaces.
You could use this method to add functions to some objects externally, which is different from adding methods to the class itself.
https://stackoverflow.com/a/2938020/2277620
Another not-so-good-way is it create a multiple subclasses so every level of subclasses add only a 'group' of functions.
I used this trick in a single case where a class was more than 10k rows.
Now I've 6 levels of inheritance where every level add a single 'category' of functions the the "previous level" class.
Not so cpu-consuming, ugly to see, not so difficult to maintain
It is highly dependant on what you wish to achieve. I had this bright idea doing what you wish to do. I likes the key words set, get run delete etc. Suppose there was a class and I wanted set of delete. As Ph.T pointed out class needs to be in single file
So make abc->set in one file and abc->get in another file made no use and sense.
So had to lot of rethinking and think of classes as micro services doing specific tasks broken down in functionality.
Solution went with name spacing.
namespace A;
class Get{public static function myCode(){}
public static function myAnotherCode(){}}
this would then be called A\Get::myCode(); or A\Get::myAnotherCode();
Similarly set would be
namespace A;
class Set{public static function myCode(){}
public static function myAnotherCode(){}}
This would then be called A\Set::myCode(); or A\Set::myAnotherCode();
Then user spl_autoload_register to load classes remember to replace \ with /
spl_autoload_register(function ($class) {
require_once __DIR__ . '/' . strtolower(str_replace('\\', '/', $class) . '.php')
});
and directory structure is as following
main (folder)
-- A (folder)
-- set.php (file)
-- get.php (file)
we asked for A\Get::myCode()
require would see it as A/Get.php then run its logic whether it is right name space or class etc.
good side of it if planned out right is it would force you to run things run in logical not just wishy washy there and there. Second you would only load functionality what you need not just whole bunch of this and that.
down side two parts of classes wont share between them unless one extends one from another which would defy the whole logic because other one would be loaded as a consequence any ways.
Careful negation has to be made as in what logic and planning data will be shared and has to be worked out before hand.
namespace A;
class Universal {
protected static $share_one;
protected static $share_two;
}
namespace A;
class Set extends Universal {
public static function myCode(){ parent::$share_one ='wow';}
}
namespace A;
class Get extends Universal {
public static function myCode(){ return parent::$share_one;}
}
main (folder)
-- A (folder)
-- universal.php (file)
-- set.php (file)
-- get.php (file)
run the code and see the magic
A\Set::myCode();
echo A\Get::myCode();
if you only need one set of functionality e.g. get then just use get echo A\Get::myCode(); would work without errors.
To be honest without playing around with name spaces its just expenditure of time earning headaches.
Side note: if done right it can speed up things as class universal wont load twice. even if called gazillion times whats what we want load defaults and then populate them where ever we need them. I have made huge classes where besides defaults only needed few functions rest was just memory load. breaking down just spead up things.

How does the keyword "use" work in PHP and can I import classes with it?

I have a file with a class Resp. The path is:
C:\xampp\htdocs\One\Classes\Resp.php
And I have an index.php file in this directory:
C:\xampp\htdocs\Two\Http\index.php
In this index.php file I want to instantiate a class Resp.
$a = new Resp();
I know I can use require or include keywords to include the file with a class:
require("One\Classes\Resp.php"); // I've set the include_path correctly already ";C:\xampp\htdocs". It works.
$a = new Resp();
But I want to import classes without using require or include. I'm trying to understand how use keyword works. I tried theses steps but nothing works:
use One\Classes\Resp;
use xampp\htdocs\One\Classes\Resp;
use htdocs\One\Classes\Resp;
use One\Classes;
use htdocs\One\Classes; /* nothing works */
$a = new Resp();
It says:
Fatal error: Class 'One\Classes\Resp' not found in C:\xampp\htdocs\Two\Http\index.php
How does the keyword use work? Can I use it to import classes?
No, you can not import a class with the use keyword. You have to use include/require statement. Even if you use a PHP auto loader, still autoloader will have to use either include or require internally.
The Purpose of use keyword:
Consider a case where you have two classes with the same name; you'll find it strange, but when you are working with a big MVC structure, it happens. So if you have two classes with the same name, put them in different namespaces. Now consider when your auto loader is loading both classes (does by require), and you are about to use object of class. In this case, the compiler will get confused which class object to load among two. To help the compiler make a decision, you can use the use statement so that it can make a decision which one is going to be used on.
Nowadays major frameworks do use include or require via composer and psr
1) composer
2) PSR-4 autoloader
Going through them may help you further.
You can also use an alias to address an exact class. Suppose you've got two classes with the same name, say Mailer with two different namespaces:
namespace SMTP;
class Mailer{}
and
namespace Mailgun;
class Mailer{}
And if you want to use both Mailer classes at the same time then you can use an alias.
use SMTP\Mailer as SMTPMailer;
use Mailgun\Mailer as MailgunMailer;
Later in your code if you want to access those class objects then you can do the following:
$smtp_mailer = new SMTPMailer;
$mailgun_mailer = new MailgunMailer;
It will reference the original class.
Some may get confused that then of there are not Similar class names then there is no use of use keyword. Well, you can use __autoload($class) function which will be called automatically when use statement gets executed with the class to be used as an argument and this can help you to load the class at run-time on the fly as and when needed.
Refer this answer to know more about class autoloader.
use doesn't include anything. It just imports the specified namespace (or class) to the current scope
If you want the classes to be autoloaded - read about autoloading
Don’t overthink what a Namespace is.
Namespace is basically just a Class prefix (like directory in Operating System) to ensure the Class path uniqueness.
Also just to make things clear, the use statement is not doing anything only aliasing your Namespaces so you can use shortcuts or include Classes with the same name but different Namespace in the same file.
E.g:
// You can do this at the top of your Class
use Symfony\Component\Debug\Debug;
if ($_SERVER['APP_DEBUG']) {
// So you can utilize the Debug class it in an elegant way
Debug::enable();
// Instead of this ugly one
// \Symfony\Component\Debug\Debug::enable();
}
If you want to know how PHP Namespaces and autoloading (the old way as well as the new way with Composer) works, you can read the blog post I just wrote on this topic: https://enterprise-level-php.com/2017/12/25/the-magic-behind-autoloading-php-files-using-composer.html
You'll have to include/require the class anyway, otherwise PHP won't know about the namespace.
You don't necessary have to do it in the same file though. You can do it in a bootstrap file for example. (or use an autoloader, but that's not the topic actually)
The issue is most likely you will need to use an auto loader that will take the name of the class (break by '\' in this case) and map it to a directory structure.
You can check out this article on the autoloading functionality of PHP. There are many implementations of this type of functionality in frameworks already.
I've actually implemented one before. Here's a link.
I agree with Green, Symfony needs namespace, so why not use them ?
This is how an example controller class starts:
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class WelcomeController extends Controller { ... }
Can I use it to import classes?
You can't do it like that besides the examples above. You can also use the keyword use inside classes to import traits, like this:
trait Stuff {
private $baz = 'baz';
public function bar() {
return $this->baz;
}
}
class Cls {
use Stuff; // import traits like this
}
$foo = new Cls;
echo $foo->bar(); // spits out 'baz'
The use keyword is for aliasing in PHP and it does not import the classes. This really helps
1) When you have classes with same name in different namespaces
2) Avoid using really long class name over and over again.
Using the keyword "use" is for shortening namespace literals. You can use both with aliasing and without it. Without aliasing you must use last part of full namespace.
<?php
use foo\bar\lastPart;
$obj=new lastPart\AnyClass(); //If there's not the line above, a fatal error will be encountered.
?>
Namespace is use to define the path to a specific file containing a class e.g.
namespace album/className;
class className{
//enter class properties and methods here
}
You can then include this specific class into another php file by using the keyword "use" like this:
use album/className;
class album extends classname {
//enter class properties and methods
}
NOTE: Do not use the path to the file containing the class to be implements, extends of use to instantiate an object but only use the namespace.

cascading use namespace in php with child classes

I am using an aliased and namespaced class in a parent class successfully but it doesn't seem to be available in the child class. The actual error is from the autoloader. The weird thing is that the function does work in the parent class and loads fine. How can I make a class brought in by use available in subclasses?
edit: the recipes are stateless -- would it make sense to make them singletons in Base and then reference them as members in the child class MyTest?
I have the two files:
Base.php:
namespace selenium;
use selenium\recipe\Cms as Cms;
class Base extends \PHPUnit_Framework_TestCase
{
public function __construct()
{
Cms::staticfunc(); //works fine
}
}
MyTest.php:
class MyTest extends \selenium\Base
{
public testMyTest()
{
Cms::staticfunc(); //errors here
}
}
From comment:
i was hoping for a way to cascade the use without duplicating that line among the 20 or so child classes
That is one of the biggest issues I have with PHP namespacing, that you have to call use for every file the current script needs access to. It's the same situation we used to face having to call require_once 20 times on some scripts in order to bring in the necessary libraries.
What I prefer to do is namespace my files (as they reside on the filesystem, like Zend Framework does) and use an autoloader to avoid the whole mess. I currently use ZF autoloader, which can be used outside of the framework, or you can also use the vanilla PHP implementation using SplAutoload.
-- Update --
I have a library which I have written over the last few years which is namespaced as Hobis_Api, and are located on the filesystem with the same convention; ~/projects/projects/dp/hobis/lib/Hobis/Api/*. In order to register the namespace with Zend_Loader I do the following:
// Be sure to set the include path to include the Zend and Hobis_Api files
// Not sure how your setup is, but would look something like:
set_include_path(get_include_path() . ':' . DIRNAME(__FILE__));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(
array(
'Hobis_Api_'
)
);
Normally the above code would go into some bootstrap file, which you can call from a centralized script in order to register the autoloader, once.
Now, if your include path is set correctly, anytime you reference Hobis_Api_* it will be autoloaded for you, so you don't need to call use or require_once, example usage:
// SomeScript.php
// Notice no requires
// I can make a call to Hobis_Api_Image without error
$image = Hobis_Api_Image;
$image->setHeight(400);

Categories