PHP namespaces not working - php

I'm trying to use PHP namespaces for the first time and can't even get a very basic example working with 2 files. Here's my directory setup:
/Framework/
/Framework/index.php
/Framework/Models/TestModel.php
And here's the code behind the two files.
index.php:
namespace Framework;
use \Framework\Models\TestModel;
$model = new TestModel();
$model->test();
TestModel.php:
namespace Framework\Models;
class TestModel
{
public function test()
{
print("test");
}
}
The error is simply that it cannot find the TestModel class:
Fatal error: Class 'Framework\Models\TestModel' not found in C:\xampp\htdocs\Framework\index.php on line 7
I'm running the PHP via a web browser at localhost/Framework/index.php. It must be something really simple I'm not seeing, can anyone point it out for me?

The Namespace on the File itself "distinguishes" from other classes and functions, however PHP/Server does not know where the physical file is simply based on a Namespace.
So including the file directly, as people has mentioned, lets PHP know exactly what you mean.
When PHP can't find the file, it will call the function spl_autoload_register() and in that method people will usually put a little function to match namespace to directory structure and then load files according.
Another option is to include Composer in your project and use the PSR-4 autoload
{
"require": {
},
"autoload": {
"psr-4": {
"App\\": "app_directoy/",
"Framework\\": "framework_directory/",
}
}
}
When including the composer autoload it will look for everything Framework/* within your framework_directory as you defined.

You should remove 'namespace Framework' and include TestModel.php instead in your index.php - Something like this:
require_once('Models/TestModel.php');
use \Framework\Models\TestModel;
$model = new TestModel();
$model->test();

Related

use function doesn't import functions in PHP

Can't import functions using the use function keywords described in PHP.net. Recreating the example locally is returning a PHP Fatal error: Uncaught Error: Call to undefined function.
composer.json
{
"autoload": {
"psr-4": {
"My\\": ""
}
}
}
full.php
<?php
namespace My\Full;
function functionName()
{
echo 'Hello Stackoverflow';
}
index.php
<?php
require 'vendor/autoload.php';
use function My\Full\functionName as func;
func();
Note: I understand I can require the file, but I wanted to know if it was possible without doing so.
use function does not include any files or function definitions it simply aliases a fully qualified function name meaning when you call the function you don't need to specify the namespace.
In your example you are using composer which is great for automatically including files however from https://www.php-fig.org/psr/psr-4/ PSR-4 is
a specification for autoloading classes from file
paths
It does not autoload functions or files which don't conform to this specification.
You can however use composer to automatically include files for situations like this. You need to update your composer.json then run composer dumpautoload
composer.json
{
"autoload": {
"files": ["full.php"]
}
}
The rest of your can then remain unchanged and it should work.

Autoload namespaces based on directory structure

According to the top comment on the PHP page spl_autoload_register( ) :
Good news for PHP 5.3 users with namespaced classes:
When you create a subfolder structure matching the namespaces of the >containing classes, you will never even have to define an autoloader.
<?php
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();
?>
However, when I have the following structure:
* classes/someclass.php
* index.php
Where someclass.php contains the following:
<?php
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
and index.php contains:
<?php
spl_autoload_extensions(".php");
spl_autoload_register();
new classes\someclass;
?>
Then I get the following error:
Fatal error: spl_autoload(): Class classes\someclass could not be
loaded
Am I getting this wrong? How can I make this work?
From the comments
This doesn't work either for the class:
<?php
namespace classes;
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
In your someclass.php file you must define the namespace at the begginning.
<?php
namespace classes;
TLDR; It works, but:
namespace classes; is missing in classes/someclass.php
set_include_path(__DIR__); is missing in index.php
(spl_autoload_extensions(".php") is not necessary)
Include Path
The SPL autoload implementation is include path based. Using a dot as include path is relative to the working directory (!) which is independent from script file location. __DIR__ names the exact directory that is needed if the classes folder lies next to the index.php file as in the scenario.
Directory Separator mapping
Next the autoloader implementation does map the class namespace separator properly on Unix systems. In case that got lost in the o/p, the PHP source code clearly has this.
Case Sensitivity of File Names
What the source code also shows is that file-names to load are made lowercase. That is, if your file system is case sensitive, the file and directory names must be in lower case.
Refernces:
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#spl_autoload
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#307
PHP - most lightweight psr-0 compliant autoloader
The user, SedaSoft, who had posted the Good news comment you which you refer, had subsequently posted another comment, due to a revision of ideas:
What I said here previously is only true on Windows. The built-in
default autoloader that is registered when you call
spl_autoload_register() without any arguments simply adds the
qualified class name plus the registered file extension (.php) to each
of the include paths and tries to include that file.
Example (on Windows):
include paths:
- "."
- "d:/projects/phplib"
qualified class name to load:
network\http\rest\Resource
Here's what happens:
PHP tries to load
'.\\network\\http\\rest\\Resource.php'
-> file not found
PHP tries to load
'd:/projects/phplib\\network\\http\\rest\\Resource.php'
-> file found and included
Note the slashes and backslashes in the file path. On Windows this
works perfectly, but on a Linux machine, the backslashes won't work
and additionally the file names are case-sensitive.
That's why on Linux the quick-and-easy way would be to convert these
qualified class names to slashes and to lowercase and pass them to the
built-in autoloader like so:
<?php
spl_autoload_register(
function ($pClassName) {
spl_autoload(strtolower(str_replace("\\", "/", $pClassName)));
}
);
?>
But this means, you have to save all your classes with lowercase file
names. Otherwise, if you omit the strtolower call, you have to use the
class names exactly as specified by the file name, which can be
annoying for class names that are defined with non-straightforward
case like e. g. XMLHttpRequest.
I prefer the lowercase approach, because it is easier to use and the
file name conversion can be done automatically on deploying.
Note that this commenter had actually posted an answer here on SO today, containing the above links (along with a short explantation), but it was subsequently deleted, whilst in the review queue - presumably due to its brevity. I have reinstated the commenter's answer, along with the content of the link. Their answer was as follows:
I wrote that comment on php.net some time ago when I was working on a
Windows system. Later, I had to partially revoke what I wrote in that
comment on the very same page in another comment, which also contains
a possible solution to the problem that is the easiest I could think
of (apart from using Composer).
Here is a screenshot of the original answer:
Do not roll your own autoloading, but use composer instead.
Create a composer.json in the root of your project:
{
"autoload": {
"psr-4": {
"classes\\": "classes/"
}
}
}
Install composer, then run
composer dump-autoload
In your index.php, require the autoloader:
require __DIR__ . '/vendor/autoload.php';
For reference, see
https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx
https://getcomposer.org/doc/01-basic-usage.md#autoloading
http://www.php-fig.org/psr/psr-0/
http://www.php-fig.org/psr/psr-4/
Do not use the spl_autoload_extensions () function if the files to load only have the php extension.
In my case, I create a class called autoload .. something similar to this:
<?php
class Autoload
{
private static $_extensions = array(
0 => '.inc',
1 => '.lib.php',
2 => '.class.php',
);
public function __construct()
{
self::set_include_path();
spl_autoload_extensions(implode(',', self::$_extensions));
spl_autoload_register(__CLASS__, 'load_class');
}
public static function set_include_paths()
{
set_include_path(implode(PATH_SEPARATOR, array(
realpath('classes');
realpath('system');
...
get_include_path();,
)));
}
public function load_class($class)
{
if (!empty($class)) {
spl_autoload($class);
}
return false;
}
}
The use of ?> At the end of the file is not necessary.

PHP Composer include file with defined "use namespace as" statements, append to each file

I know you can use Composer's files scheme to include functions or definitions automatically in every file.
I'd like to take this one step further and never have to manually write a use classname as statement again in individual files. This is my ideal set up:
/composer.json:
{
"require": {
"slim/slim": "2.*"
}
"autoload": {
"files": ["init.php"]
}
}
/init.php:
use Slim\Slim as Slim;
use Slim\Route as Route;
define("RYAN","BRODIE");
/example.php:
require '/vendor/autoload.php';
echo RYAN; // ✔ outputs "BRODIE"
new Slim(); // × Fatal error: Class 'Slim' not found
This way whenever I add a new namespace or class to an existing namespace, I can add it to init.php and then use it as I so wish across all of the repository.
Unless you modify the PHP runtime, this is not possible. namespace and use declarations are only valid in the physical file they appear in. These declarations do not span across includes. If they did you'd have a real nightmare managing namespaces and aliases.
Declaring these at the top of each file is simply something you'll have to live with. Most languages that have features similar to PHP's namespaces do the same.
Link to documentation.

How to Include namespaced Files in PHP?

I have a Problem developing in PHP. First I have to say that I'm not the experienced PHP developer on this Planet.
My Code/Problem is as followed:
In file Controllers\TestController.php:
<?php
namespace My\Test\Controllers;
class TestController
{
public function HelloTest()
{
echo 'Hello!';
}
}
?>
When I want to include this class in another php file like this
File Models\TestModel.php:
<?php
namespace My\Test\Models;
use My\Test\Controllers;
class TestModel
{
public function TestModelFunction()
{
$control = new TestClass();
$control->HelloTest();
}
}
?>
File index.php_
<?php
use My\Test\Models;
$model = new TestModel();
$model->TestModelFunction();
?>
That just won't work... I'll always get the following error:
Class 'TestModel' not found!
When I now add:
include_once 'Models/TestModel.php' in index.php
AND
include_once '..Controllers/TestController.php' in TestModel.php
then it works...
Folder Structure:
Project
|-Models
| TestModel.php
|-Controllers
| TestController.php
|index.php
But do I really have to specify every Time where the files are?
Yes you will always have to include the files that define your classes.
The namespace is just a way to package your classes together, not a way to automatically include PHP files.
If your are looking for a way to automatically include PHP files when needed, have a look on autoload.
Namespaces logically separate code into different... well, namespaces. It has nothing to do with including the files that contain code for that namespace. So yes, you do need to include the file in some way or another (e.g. autoloaders) in addition to namespacing them.
Another approach is to use autoloader (http://php.net/manual/en/language.oop5.autoload.php). You can find some good open source autoloaders out there.

PHP class not found but it's included

I'm including a PHP class with
require_once($ENGINE."/classUser.php");
but when the code is executed i receive this error:
Fatal error: Class 'User' not found in
C:\xampp\htdocs\WebName\resources\engine\ajax\signup.php on line 12
I still can't figure out what's the problem. I'm 99% sure it's correct.
The "$ENGINE" is correct, and the class is correct too (Netbeans suggests me class methods and variables).
signup.php:
<?php
/* Created on: 13/12/2011
* Author:
*
* Description: User signup procedure.
*/
require_once("../settings.php");
require_once($ENGINE."/classUser.php");
$user = new User();
$user->createUser($_POST["username"], $_POST["email"], $_POST["password"]);
?>
classUser.php:
<?php
/* Created on: 13/12/2011
* Author:
*
* Description: This class manages users.
*/
require_once("settings.php");
require_once($LIBRARY."/cassandraphp/cassandra.php");
class User {
public function createUser($username, $email, $password){
$cassandra = Cassandra::createInstance($CASSANDRASERVER);
$cassandra->set(
"user.".$username,
array(
'ID' => uniqid(),
'Username' => $username,
'Email' => $email,
'Password' => $password
)
);
}
}
?>
Check to make sure your environment isn't being picky about your opening tags. My configuration requires:
<?php
If I try to use:
<?
Then I get the same error as you.
Short tags can be enabled through the INI setting short_open_tag.
if ( ! class_exists('User'))
die('There is no hope!');
I had this problem and the solution was namespaces. The included file was included in its own namespace. Obvious thing, easy to overlook.
It may also be, that you by mistake commented out such a line like require_once __DIR__.'/../vendor/autoload.php'; --- your namespaces are not loaded.
Or you forget to add a classmap to the composer, thus classes are not autoloaded and are not available. For example,
"autoload": {
"psr-4": {
"": "src/"
},
"classmap": [
"dir/YourClass.php",
]
},
"require": {
"php": ">=5.3.9",
"symfony/symfony": "2.8.*",
First of all check if $ENGINE."/classUser.php" is a valid name of existing file.
Try this:
var_dump(file_exists($ENGINE."/classUser.php"));
The problem went away when I did
sudo service apache2 restart
My fail might be useful to someone, so I thought I would post as I finally figured out what the issue was. I was autoloading classes like this:
define("PROJECT_PATH", __DIR__);
// Autoload class definitions
function my_autoload($class) {
if(preg_match('/\A\w+\Z/', $class)) {
include(PROJECT_PATH . '/classes/' . $class . '.class.php');
}
}
spl_autoload_register('my_autoload');
In my /classes folder I had 4 classes:
dbobject.class.php
meeting.class.php
session.class.php
user.class.php
When I later created a new class called:
cscmeeting.class.php
I started getting the can't load DbObject class. I simply could not figure out what was wrong. As soon as I deleted cscmeeting.class.php from the directory, it worked again.
I finally realized that it was looping through the directory alphabetically and prior to cscmeeting.class.php the first class that got loaded was cscmeeting.class.php since it started with D. But when I add the new class, which starts with C it would load that first and it extended the DbObject class. So it chocked every time.
I ended up naming my DbObject class to _dbobject.class.php and it always loads that first.
I realize my naming conventions are probably not great and that's why I was having issues. But I'm new to OOP so doing my best.
As a more systematic and structured solution you could define folders where your classes are stored and create an autoloader (__autoload()) which will search the class files in defined places:
require_once("../settings.php");
define('DIR_CLASSES', '/path/to/the/classes/folder/'); // this can be inside your settings.php
$user = new User();
$user->createUser($_POST["username"], $_POST["email"], $_POST["password"]);
function __autoload($classname) {
if(file_exists(DIR_CLASSES . 'class' . $classname . '.php')) {
include_once(DIR_CLASSES . 'class' . $classname . '.php'); // looking for the class in the project's classes folder
} else {
include_once($classname . '.php'); // looking for the class in include_path
}
}
It 'happened to me!
The problem is that somehow you include a file with the same file name of the class thus invalidating the same class!
Check the path of inclusion and these checks files with the same name!
Check your file permissions for the correct linux user for classUser.php
Check File Permissions
Check File size.
Sometimes an inaccessible or corrupted file would be the problem, as was in my case
you should declare namespace in the ClassUser.php, something like this:
<?php
namespace app; // where 'app' is a folder declared as a root for the project
class ClassUser{
public function test(){
//log something here
}
}
?>
Then you can add the class in your other php files like this:
<?php
use app\ClassUser;
$classUserLcl = new ClassUser();
$classUserLcl->test();
?>
and you are done.
Otherwize it will abuse:
You Oh! its a Fatal error : Uncaught Error: Class 'app\ClassUser' not found in ...
When namespace declaration is part of your php class file "this kind of weird errors tends to appear".
Solution: Use namespace with {, your code shows like this:
<?php
namespace path_to\lib {
require_once "folder/php_class_file_where_namespace_declaration_is_part_of_it.php";
**YOUR CODE HERE**
<?php } ?>
Double check your autoloader's requirements & namespaces.
For example, does your autoloader require your namespace to match the
folder structure of where the file is located? If so, make sure they
match.
Another example, does your autoloader require your filenames to
follow a certain pattern/is it case sensitive? If so, make sure the
filename follows the correct pattern.
And of course if the class is in a namespace make sure to include it
properly with a fully qualified class name (/Path/ClassName) or with a use statement at the top of your file.
Maybe it is how you use new User().
Set path something like
$user = new \resources\engine\ajax\User();
If you've included the file correctly and file exists and still getting error:
Then make sure:
Your included file contains the class and is not defined within any namespace.
If the class is in a namespace then:
instead of new YourClass() you've to do new YourNamespace\YourClass()
Yes this happen in cases when use a trait or extend a class, you should be aware to instantiate the class after the class declaration for example:
This example will trigger class not found error:
$class = new A();
class A {
use SomeTrait;
}
To make it to work move the initialization step in the bottom like so:
class A {
use SomeTrait;
}
$class = new A();
I found I had to use the use keyword, as well as the include statement. Tested it with either missing, and it doesn't work.
namespace foo;
use src\config\object;
include('config/object.php');

Categories