I'm trying to set up a simple example in order to understand how the ClassLoader Component of Symfony and the new PSR-0 standard work.
First I created Bar.php:
namespace Acme;
class Bar
{
// Implementation
public static function helloWorld() {
echo "Hello world!";
}
}
Then I created a autoloader.php (under the vendor path I have the ClassLoader component):
require_once __DIR__.'/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->register();
$loader->registerNamespace('Acme', __DIR__);
Lastly I created Foo.php:
require_once 'autoloader.php';
use Acme;
class Foo extends Bar
{
// Implementation
}
$foo = new Foo();
$foo::helloWorld();
But when I execute:
$ php Foo.php
I get the following error message:
PHP Warning: The use statement with non-compound name 'Acme' has no
effect in Foo.php on line 4
PHP Fatal error: Class 'Bar' not found in Foo.php on line 7
What am I doing wrong here?
UPDATE:
If instead of using namespace Acme I use namespace Acme\Bar in Bar.php, I get the same error message as shown above.
I've found what was going on wrong. The problem was that the UniversalClassLoader class that follows the standard PSR-0, requires that the files with the namespaces cannot be in the root directory, and must be created under a minimum of one directory.
Here is the code in case someone wants to try the example.
autoloader.php
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$loader->registerNamespaces(array('Acme' => __DIR__ . '/src'));
$loader->register();
./src/Acme/Bar.php
namespace Acme;
class Bar
{
// Implementation
public static function helloWorld() {
echo "Hello world!";
}
}
./src/Acme/Foo.php
namespace Acme;
require_once '../../autoloader.php';
use Acme\Bar;
class Foo extends Bar
{
// Implementation
}
$foo = new Foo();
$foo::helloWorld();
You can't use an entire namespace, you have tu use Acme\Bar.
Related
I have been trying to use and understand namespace and traits but getting this error:
"Trait a\b\Train not found" when I run example.php
"Trait a\b\Train not found" when I run Bayes.php
Just confused how it works and why getting error.
Here my code:
(these files store in the same folder)
//example.php
use a\classification;
include_once 'Bayes.php';
$classifier = new Bayes();
$classifier -> train($samples, $labels);
$classifier -> predict([something]);
//Bayes.php
namespace a\classification;
use a\b\Predict;
use a\b\Train;
class Bayes {
use Train, Predict
}
//Train.php
namespace a\b;
trait Train{
}
//Predict.php
namespace a\b;
trait Predict{
}
I'm sorry for my dumb question, really appreciate for your help :"
You first need to include Train.php and Predict.php inside Bayes.php. You're referring to the traits, but how does PHP know about those?
Then, you need to specify a proper namespace when creating a new Bayes(). You include the file, but it's in another namespace. The use declaration at the top isn't doing much. It lets you create a new classification\Bayes() instead of new a\classification\Bayes().
Try something like this instead:
example.php
<?php
use a\classification\Bayes;
include_once 'Bayes.php';
$classifier = new Bayes();
$samples = "text";
$labels = "text";
$classifier -> train($samples, $labels);
$classifier -> predict(["something"]);
Bayes.php
<?php
namespace a\classification;
require_once 'Train.php';
require_once 'Predict.php';
use a\b\Predict as P;
use a\b\Train as T;
class Bayes
{
use T, P;
}
Train.php
<?php
namespace a\b;
trait Train
{
public function Train()
{
echo "training";
}
}
Predict.php
<?php
namespace a\b;
trait Predict
{
function predict()
{
echo "predicting";
}
}
Note the use of an alias in Bayes.php: use a\b\Predict as P; This is where aliases can be helpful to simplify code. If the alias isn't specified, it just uses the last name without namespaces.
You're running into trouble because you're not requiring the files you need, so PHP doesn't know what you're talking about when you ask it to use some of these classes.
example.php requires access to Bayes.php
use a\classification;
require_once 'Bayes.php';
Bayes.php requires access to Train.php and Predict.php
namespace a\classification;
require_once 'Train.php';
require_once 'Predict.php';
use a\b\Predict, a\b\Train;
class Bayes {
use Train, Predict
}
Note: use require_once rather than include_once when the external resource is necessary.
I can not understand why php gives me an error
"Fatal error: Cannot declare class rex\builder\RexBuilder, because the
name is already in use in /var/www/site2.dev/App/rex/RexBuilder.php on
line 12"
RexBuilder static class, and it is called only 1 time.
I did a search on the project, no longer classes with the same name.
<?php
namespace rex\builder;
require_once 'Router.php';
use rex\router\Router;
error_reporting(E_ALL);
ini_set('display_errors', 1);
class RexBuilder {
public static function collector($array) {
$router = new Router();
foreach ($array as $key => $val) {
$router->get($val->getMethod(), $val->getInterfaces(), $val->getHandler());
}
$router->init();
}
}
?>
Call the class in index.php
RexBuilder::collector(array(
new BuildModel('POST', '/api/v1/user/register', new \api\register\Registration()),
new BuildModel('POST', '/api/v1/user/login', new \api\login\Login())));
More This class is not used
The error is thrown because of the use rex\router\Router; duplicate classes.
When you are writing use namespace.. it means you can go directly to that namespace like it is your current namespace
Lets take a look at the next code:
We'll create a file and declare that it belongs to namespace classes\a
//file: a.php
<?php
namespace classes\a;
class A{
}
now lets create another file b.php (and declare it belongs to namespace classes\b but it means nothing for the example)
namespace classes\b;
require_once "a.php";
use classes\a; //Notice that I'm using this namespace, it means I can use it directly
class A{
}
Generates the error
Fatal error: Cannot declare class classes\b\A because the name is already in use in
We have to solutions possible:
First: remove the use tag and write the namespace directly
class A{
function __constructor(){
$instance = new classes\a\A();
}
}
Second, give it alias
use classes\a as out_a;
class A{
function __constructor(){
$instance = new out_a\A();
}
}
For your code, just remove the use or give it an alias.
The problem is certainly because you include the RexBuilder.php file two times instead of one.
If you call the file by this way : include('RexBuilder.php'); or this way require('RexBuilder.php'); please change it by include_once('RexBuilder.php'); or require_once('RexBuilder.php'); which only allows ONE call of the file.
I have found several SO questions similar to mine, but am struggling to find an answer that helps me, plus I'd really like to know the best practice for autoloading classes that exist within namespaces.
My folder structure:
root
-- classes
--- Users
---- Users.class.php
And users.php;
<?php
namespace CompanyName\ProjectName\Users;
class UserMapper
{
// class code here
}
And my autoload function, which sits in the root folder;
/* autoload classes on instatiation */
spl_autoload_register(function($class)
{
include $_SERVER['DOCUMENT_ROOT'] . '/classes/' . $class . '.class.php';
});
And, let's say I call the user class like so;
<?php
new \CompanyName\ProjectName\User();
Warning: include(/Applications/XAMPP/xamppfiles/htdocs/test_tool/classes/CompanyName\ProjectName\User.class.php): failed to open stream: No such file or directory in...etc
To use spl_autoload_register, do I need to map my folder structure to my namespace structure? I would prefer not to do this as I like to have my classes in the same folder, with sub folders within.
Or do I add extra code to my autoload function?
I have also searched the php manual, and there is no working namespace example, which I find very strange.
Any help would be much appreciated.
Thanks in advance.
Disclaimer: I am a beginer at php, my answer may not be correct
but i'm confident examining and testing the example bellow will help clarify the use of Namespaces with spl_autoload_register for any beginner like me.
Consider this folder structure :
root/ contains index.php.
root/model/ contains A.php & AA.php
A.php :
?php
namespace company\model;
use \company\model\A;
class A
{
public function speak()
{
echo 'hello world! ';
}
}
AA.php :
?php
namespace company\model;
use \company\model\A;
require_once 'A.php';
class AA extends A
{
public function shout()
{
echo 'HELLO WOORLD!!!';
}
}
index.php :
<?php
namespace company;
use \company\model\A;
function classLoader ($className)
{
if (file_exists($className.'.php'))
{
require_once $className.'.php';
} else {
$className = str_replace('\\', '/', $className);
$className = str_replace('company/', '', $className);
if (file_exists($className.'.php'))
require_once $className.'.php';
else
throw new EXCEPTION('classLoader could not find '.$className.'.php .');
}
}
spl_autoload_register(classLoader);
$obj = new A;
//we dont need to write ($obj = new \company\model\A;)
//because of statement at line 4
$obj->speak();
echo '<br/>';
$objA = new \company\model\AA;
$objA->shout();
echo '<br/>';
class AB extends \company\model\AA
{
public function doBoth()
{
$this->speak();
$this->shout();
}
}
$objB = new AB;
$objB->doBoth();
I'm having trouble getting autoloading to work with namespaces. Here's the directory structure I created for this:
index.php
app/
utils/
sys/
DirReader.php
helpers/
DB.php
The index.php contains the autoloader, which includes the files DirReader.php and DB.php.
Here's what the index.php looks like:
<?php
function __autoload($ns_str) //ns_str = namespace string
{
$path = str_replace('\\', DIRECTORY_SEPARATOR, $ns_str);
//echo "**$path**\n";
require_once "$path.php";
}
use \app\utils\sys as sys;
use \app\utils\helpers as helpers;
$dir = new sys\DirReader();
$db = new helpers\DB();
Here's DirReader.php:
<?php
namespace app\utils\sys;
class DirReader
{
public function __construct()
{
echo "DirReader object created!\n";
}
}
And here's DB.php:
<?php
namespace app\utils\helpers;
class DB
{
public function __construct()
{
echo "DB object created!\n";
}
}
The example works fine, but when I add a namespace declaration to index.php, it fails:
<?php
namespace myns;
function __autoload($ns_str) //ns_str = namespace string
{ /*. . .*/
PHP Fatal error: Class 'app\utils\sys\DirReader' not found in
/var/www/html/php_learn/autoloading_1/index.php on line 15 PHP Stack
trace: PHP 1. {main}()
/var/www/html/php_learn/autoloading_1/index.php:0
According to me this error shouldn't come because I've used absolute names when using namespaces in index.php. I know that saying something like use app\utils\sys as sys; would fail because then the namespace will be searched relative to myns, where nothing exists. But I don't know why my code is not working. (I also tried changing the name of the namespace in index.php to autoloading_1, the name of the containing directory, but it didn't help).
The __autoload function must be defined in global space.
Use spl_autoload_register otherwise. Use of __autoload() is discouraged.
recently I was learning zend framework 2, and there's a problem annoying me for a long time, things look like this:
<?php
namespace Album\Model;
// Add these import statements
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface
{
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
// Add content to these methods:
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
//....
?>
This code was a section of the "skeleton application" programme, which was a tutorial of ZF2. The first time I see the programme, I don't understand what's the usage of "namespace" and "use", because this two keyword doesn't exist in php5.2(also the same in the earlier edition), so I go to see the manual and try to understand it.I write a programme to simulate what really happens:
<?php
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the programme above works well, of course I created two folders named script and lib, and there's a file named test.php.
Seems like every thing is clear, zend framework also has a autoload function, BUT when I noticed the codes in "skeleton application programme", there was a namespace in the beginning, so I adds the namespace to my programme too:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the page returned me inforamtion as following:
Fatal error: Class 'script\lib\test' not found in E:\wamp\www\test\test_29.php on line 6
I tried to change the namespace's name such as script\lib, script\lib\test...
but it's useless.
Any answer will be appreciated, thanks.
Now I will give you more details about this issue:
To understand the usage of "namespace" and "use", I looked over the materials on php.net:
http://php.net/manual/en/language.namespaces.importing.php
In this page, there was a section of code looks like this:
Example #1 importing/aliasing with the use operator
<?php
namespace foo;
use My\Full\Classname as Another;
// this is the same as use My\Full\NSname as NSname
use My\Full\NSname;
// importing a global class
use ArrayObject;
$obj = new namespace\Another; // instantiates object of class foo\Another
$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // instantiates object of class ArrayObject
// without the "use ArrayObject" we would instantiate an object of class
?>
Now let's review the programme I write in the above:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
It's the same, I'm trying to simulate that instance, if we don't use the autoload function:
<?php
namespace test;
use script\lib\test;
require_once 'script/lib/test.php';
$o = new test();
echo $o->getWelcome();
?>
It works well too, BUT when I use __autoload function to load the class file, there's something wrong.
I don't konw where's problem, OR any body tried to write an instance to put the "Example #1" into practice? I will wait for your answer.
I think you're misunderstanding what's going on here.
Namespaces allow you to, more or less, create "directories" for your classes. So you can create the \Foo class and the \Test\Foo class (where \ represents the "root" of your application).
The way autoloading works is that your files mirror your namespacing. So foo.php would be in the root of your autoloading but you would create /test/foo.php for \Test\Foo
The use keyword has two uses. One is to alias class files and the other is, in PHP 5.4 or later, to bring in a Trait into your current class.
Now, to your question. First, Let's look at your code
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
This is confusing. You declare a namespace (which you don't need to do here) but then you alias it to script\lib\test. PHP is now looking for a file called /script/lib/test.php, which your error message says doesn't exist. But you said the file does exist so let's look at that
public function getWelcome() {
return 'welcome';
}
This isn't a class. It's a function. For this example you need a complete class
<?php
namespace script\lib;
class test {
public function getWelcome() {
return 'welcome';
}
}
Lastly, let's talk autoloading. You don't need to use use with autoloading. Your autoloader should take care of that for you. You should, however, use spl_autoload_register(), as __autoload() is soon to be depreciated.
From ZF2 docu
Zend\Loader\StandardAutoloader is designed as a PSR-0-compliant autoloader. It assumes a 1:1 mapping of the namespace+classname to the filesystem, wherein namespace separators and underscores are translated to directory separators.
Read more about: PSR-0
So if you're using namespaces the classname that gets send to the autoloader doesn't look like test. It looks like YOUR_NAMESPACE\test. YOUR_NAMESPACE is the namespace that you defined in the class with namespace YOUR_NAMESPACE;
PSR-0 is a standard that says: Your namespace should reflect your filesystem. You only have to replace the backslashes with forward slashes. Or _ with / if you're using pseudo namespaces like in ZF1. (Album_Model_Album)
So output the $className that is sent to your autoloader and you will see..