cascading use namespace in php with child classes - php

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);

Related

Codeigniter Custom Libraries and Namespaces

I have been creating custom libraries for a project that i've been porting over into the CI framework and I ran into an issue where certain classes have identical names.
To circumvent this issue, I tried implementing namespaces to no avail. I've been doing research and I know in the past this may not have been possible but with the newer version of PHP, I was wondering if there was a way to do this or if I was doing it correctly.
CI Version: 2.1.4
PHP Version: 5.4.12
Here is a demo of my setup:
application/libraries/class1.php
<?
class class1{
public function __construct()
{
$CI =& get_instance();
$CI->load->library('foo/class2.php');
}
}
?>
application/libraries/foo/class2.php
<?
namespace foo
class class2{
function bar(){
}
}
?>
When I run my CI application, I will get the following error:
Non-existent class: class2
Thanks for any help.
From what I've found, if the library file doesn't have
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
as the first line of code it won't load the library.
But then the namespace declaration needs to be above that.
CodeIgniter was written for PHP4 whereas namespace is PHP5.
There's more information in this thread: Namespace in PHP CodeIgniter Framework
Codeigniter does not support namespaces in 2.x and 3.x, what I would usually do especially with 3rd party libraries is load them in manually. Using your example, I would do:
// you can still manually load in libraries like this
require_once(APPPATH.'libraries/foo/class2.php');
class class1 {
public function __construct()
{
$CI =& get_instance();
// instantiate the class2 and make it available for all of class1 methods
$this->class2 = new foo/class2();
}
}
Just because it's a framework doesn't prevent you from using core php functionality, most people forget that you can still do normal php methods to achieve the same results.
This issue arose in a personal project while attempting to use some third-party libraries.
To get around it (without modifying the source files), I made a "bootstrap" class that loaded and extended the core library:
<?php
require_once(APPPATH.'libraries/foo/class2.php');
class class2 extends foo\class2 {}
This "bootstrap" class can then be loaded and used as if it were the extended one:
$this->load->library("class2");
$this->class2->bar(); // same as foo\class2->bar();
The issue is that 'load' doesn't take into account namespaces as far as I know.
which means that load('foo/class2') will look for the folder 'foo' inside the libraries folder.
you include the files normaly, when you create a new object you use the 'foo/bar'.
I'm not if the load class supports that though, You might need to simply create a new object manually(which is what the load class does anyway, it includes the file and creates a new object).
I'll quote an answer from Alex.
I know I've answered this question before I just can't find where and don't know what to search. So here it is: Codeigniter can only load single php file libraries (excluding drivers which is a different thing entirely). To load this kindof library (namespaced) you have to use something like: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md (class example).
Let's call it Autoloader_psr4 and save it in libraries (modify the class declaration to match this name verbatim (e.g. Autoloader_psr4). Remove the namespace declaration in the class so it looks like: https://pastebin.com/NU8Rbp7Y
Let's also move all the files in src/randomorg/ or src/foo to just be in a folder in third_party called RandomOrg or foo e.g. application/third_party/RandomOrg or application/third_party/foo. Your folder should look like the contents here: https://github.com/defiant/randomorg/tree/master/src/randomorg
Usage:
$this->load->library('autoloader_psr4');
$this->autoloader_psr4->register();
$this->autoloader_psr4->addNamespace('RandomOrg', APPPATH . 'third_party/RandomOrg');
$random = new \RandomOrg\Client(); // or whatever...

inheritance and external files. How do I go about it?

I have 3 files.
vehicleClass.php
motorbikeClass.php (extends vehicleClass)
index.php
My question is... How do I connect all 3. On my index page, do I have to have
include 'classes/vehicleClass.php';
Do I need to have an include for the motorbike class or will the extend (inheritence) cover this?
You can let php autoload your files, by registering your own autoload function. If you have, in example, all your class files in the directory DOCROOT . 'classes' (DOCROOT being your document root), you can use a function like this (example):
function class_loader($class) {
include DOCROOT . 'classes/' . $class . '.class.php';
}
spl_autoload_register('class_loader');
Now if you try to create an object of class 'foo', it will try to include the file DOCROOT . '/classes/foo.class.php' before creating the object. You might want to extend the function a bit, eg. to lowercase file names (include DOCROOT . 'classes/'. strtolower($class) .'.class.php';). This way you can place class Foo in foo.class.php.
Short answer: a class that is extending (or otherwise using) another class must already have defined the parent class before the definition of the child class. Your assumption is correct, your VehicleClass must be included (or better, require'd) prior to your definition of MotorBike class.
However, most frameworks don't go about and include every depedency before all class definitions. This would become unwieldy on any system that has any amount of complexity to it. Instead, the developers of PHP have provided methods for autoloading classes. Using spl_autoload_register will allow you to write a function that will attempt to load in the source file for a given class whenever it is referenced but a definition for it has not yet been found.
Furthermore, once you get a system together that becomes complex, you don't want to store all of your files in a single place. Most frameworks leverage the filesystem and namespaces to help better organize all of their classes. Because of this, the PSR-0 standard was developed in order to help facilitate autoloading between frameworks. Take a look at this question for examples of PSR-0 compliant autoloaders.
Example of PSR-0 compliant class:
<?php namespace Vendor\Package;
class ClassName { }
This file would live in the filesystem at /Vendor/Package/ClassName.php
What you have to do is include 2 files in the index.php.
For example, your index.php page could be something like this.
<?php
require 'classes/vehicleClass.php';
require 'classes/motorbikeClass.php';
// Assuming your class name is MotorBike
$motorBike = new MotorBike();
// And just call the method you want, for example If you have a method called bikeName
echo $motorBike->bikeName();
?>
I hope you get an idea now.
P/S: I prefer require over include. :) Include() should work fine too.

Autoloader appears to be omniscient | Nested includes not needed

I use this simple autoloader code:
function __autoload( $class_name ) { include 'class.' . $class_name . '.php'; }
and I make this call new SDB();
SDB actually inherits from SDBOne which I never include...yet the auto-loader loads it.
This means it knows to load modules nested / included in other modules.
So from the looks of it I never need to require or include anything if I use the auto-loader.
However, if I use a stand alone module where there is no auto-loader, I then need to include the class it inherits from.
Should I use
require,
require_once,
include,
include_once.
I would guess to go with require_once because I want an error not a warning...plus when I use the code with the autoloader I don't want it loaded twice.
Basically I have 2 types of use for SDB : one with the autoloader present and one with out.
Reference
http://php.net/manual/en/language.oop5.autoload.php
You are correct, require_once would be the correct way to include a parent class or dependent class in one file. That way if its included multiple times, the require_once prevents errors that would arise from redeclaring a class.
The autoloader is autoloading SDBOne automatically because it needs that class defined before it can extend SDB from it. The same thing is happening to autoload the parent class on demand as happens when you try to load the inherited class.
Also, you should consider switching to spl_autoload_register so that your code will work well with other code that may use an autoloader. The SPL autoloader supports multiple autoloaders and creates a stack in the order they are registered. This way if the first autoloader doesn't satisfy the requirement, it keeps trying subsequently registered autoloaders until the class is loaded or cannot be found.
Another note on preventing errors, you may want to change your autoload function as follows:
function __autoload( $class_name ) {
$file = 'class.' . $class_name . '.php';
if (file_exists($file)) {
include $file;
}
}
Because if the class being autoloaded doesn't exist, you will get errors about including a non-existent file. This is especially important when using spl_autoload_register; you don't want your autoloader to emit unnecessary warnings about missing files if another autoloader will be ultimately responsible for loading the class in question.
Hope that helps.

Does extending a parent class in PHP require the file with the class being included?

Should I include/require_once the parent class that I am extending in PHP?
for example I have a class called Shapes
class Shapes {
}
And then I created a subclass called
require_once('shapes.php');
class Circle extends Shapes {
}
Should I require the parent class when I am extending? or should just use extends the subclass to itss parent class even though they are in the same folder?
You need to do something in order to let PHP see your base class definition before it can process the child class, otherwise a fatal error will occur.
This something can be either a manual require_once of the base class file, or autoloading (there are other options for autoloading, but spl_autoload_register is the one you should use).
Which approach to use depends on the scope: when coding a small test project setting up autoloading is probably overkill. But as the code base gets larger and larger, autoloading becomes more attractive because:
it can hide complex source file resolution logic (e.g. if you have a configurable directory for the base classes; there are more advanced scenarios as well)
it can be configured incrementally: you can use multiple autoloaders that run as a chain, and each independent module of the application can register its independent autoloader that coexists peacefully with all the others
Yes, you have to include it if that class is not declared in the same file.
Also there was a feature called Autoloading Classes with which you can create a function like this one:
function __autoload($class){
require_once('classes/' . $class . '.php');
}
And it will automatically include classes which are not found in the existing scope
Also you can read about this feature too: autoload_register
You can also use composer to simplify the process.
Make composer.json like this
{
...
"autoload": {
"psr-4": {
"": "src/"
}
},
...
}
Get composer from https://getcomposer.org/ and run composer install.
You should load composer's autoload script once like
require_once __DIR__ . '/vendor/autoload.php';
If you have PHP less than 5.3.0 then replace __DIR__ with dirname(__FILE__).
And put your files to src folder. For example if you have the class Acme\Utils\FooBar then it should be in src/Acme/Utils/FooBar.php.

Extending classes in 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();
}
}

Categories