Reputation: 13
I'm using PhpStorm to develop an iCal Manager.
I have a class User
defined in the namespace Engine
used for all the website, and a class User
defined in the namespace iCal
used for iCal process only, extended from \Engine\User
.
When coding, PhpStorm can locate \Engine\User
in order to define \iCal\User
, but when executing I got a "Fatal Error: Class \Engine\User not found".
I'm using PhpStorm 2019.1 with PHP 7.2.4 on WampServer.
I tried to make an alias, didn't work. I tried to require all my files in one then to add it in 'Engine', didn't work either.
\Engine\User:
<?php
declare(strict_types=1);
//Created for IGPD by leroy the 27/07/2019
namespace Engine;
use PDO;
use Exception;
use PDOStatement;
/**
* Class User
* @property bool attendee
* @package Engine
*/
class User
{
public function __construct(array $user)
{
$this->email = $user['email'];
$this->name = $user['name'];
$this->surname = $user['surname'];
$this->language = $user['language'];
}
}
\iCal\User:
<?php
declare(strict_types=1);
//Created for IGPD by leroy the 27/07/2019
namespace iCal;
use Engine\User as UserAlias;
/**
* Class User
* @package iCal
*/
class User extends UserAlias
{
public function __construct(array $user, string $cutype, string $role, bool $rsvp, string $partstat, string $sentby, string $dir, string $type)
{
$this->calAddress = "mailto:" . $user['email'];
$this->cutype = in_array($cutype, ['INDIVIDUAL', 'GROUP', 'RESOURCE', 'ROOM', 'UNKNOWN']) ? $cutype : 'UNKNOWN';
$this->role = in_array($role, ['CHAIR', 'REQ-PARTICIPANT', 'OPT-PARTICIPANT', 'NON-PARTICIPANT']) ? $role : 'OPT-PARTICIPANT';
$this->rsvp = $rsvp ? 'TRUE' : 'FALSE';
$this->cn = $user['name'] . " " . $user['surname'];
$this->language = in_array($user['language'], ['EN', 'FR']) ? $user['language'] : 'EN';
$this->partstat = in_array($partstat, ['NEEDS-ACTION', 'ACCEPTED', 'DECLINED', 'TENTATIVE', 'DELEGATED']) ? $partstat : 'NEEDS-ACTION';
$this->sentby = $sentby;
$this->dir = $dir;
$this->type = $type;
UserAlias::__construct($user);
}}
I just run the class file to check for errors, so I should not get anything. The actual output is:
Fatal error: Class 'Engine\User' not found in D:\wamp\www\IGPD\iCal\User.php on line 12
Here is iCal.php
, the file that require all the files in the directory:
\iCal\iCal.php:
<?php
declare(strict_types=1);
namespace iCal;
//Created for IGPD by leroy the 02/08/2019
require_once 'Alarm.php';
require_once 'Calendar.php';
require_once 'Event.php';
require_once 'FreeBusy.php';
require_once 'Journal.php';
require_once 'Todo.php';
require_once 'User.php';
Then I used this in my main page but it's not the one I want to load so I shouldn't need it:
\Engine\Engine.php:
<?php
declare(strict_types = 1);
namespace Engine;
session_start();
require_once "collections.php";
require_once "..\iCal\iCal.php";
I do not use autoload.
Upvotes: 0
Views: 1015
Reputation: 146370
When you run php foo.php
the PHP interpreter loads that single file and no other, unless you explicitly instruct it to do otherwise. On the other side, your IDE does not need to run your code. It just scans your project's directory, compiles all classes, functions, constants, etc. and assumes they're available when you ask about them.
Any complex program calls for splitting code in several files, thus you need to address it. Almost all solutions available involve include
et al. in some way, but the details can differ vastly. It's also important to note that namespaces do not play any role in class or file loading—it's just a mechanism to have more short names available as codebases grow and third-party libraries thrive.
Your are using direct calls to require_once to load files. That's roughly equivalent to copying and pasting the included files's code in the exact location of the require_once
construct, but there's a caveat: PHP parses each file independently and if you're loading a file with a class definition that depends on another class, you need to have that other classed parsed earlier. Look at this example:
<?php // Animal.php
class Animal
{
}
<?php // Cat.php
class Cat extends Animal
{
}
This script runs flawlessly:
<?php
require_once 'Animal.php';
require_once 'Cat.php';
This one triggers Fatal error: Uncaught Error: Class 'Animal' not found in C:\tmp\Cat.php:2:
<?php
require_once 'Cat.php';
require_once 'Animal.php';
So this is your exact problem: you aren't loading your files in the correct order.
This can be tricky in relatively small projects and it doesn't scale well so that's why class auto-loading was implemented. PHP provides a hook function you can customise and calls that function whenever it finds code that needs a yet unknown class. Your custom function gets the class name as argument and does whatever it sees fit (typically involving an include
call).
Some guys wrote a spec on how an autoloader could work and called it PSR-4. The well-known Composer package manager wrote an auto-loader implementing that spec. So, if you use Composer in your projects, you can benefit from that auto-loader without the need of writing any code. All you need to do is to follow a specific naming convention and directory structure and define where your classes root directory is. So, if you want a class called \Engine\User
you define it in a file located at /Blah/Blah/Engine/User.php
and the class is loaded automatically when it's used in your code.
Upvotes: 1
Reputation: 13
I've explored the autoload solution mentioned by @ÁlvaroGonzález, and so far it's the only working solution. As a result, I came up with this code, in the index.php
at the root of my directory.
\index.php:
<?php
declare(strict_types=1);
namespace iCal;
spl_autoload_register(function ($class){
require_once ucfirst($class) . '.php';
});
$e = new Calendar("e", "e", "e", "r", "e");
$f = new User(['email' => "", 'name'=>"", 'surname'=>"", 'language'=>""], "e", "e", true, "e", "e", "e", "e");
I'm glad to say it worked, but I'm afraid that is not the best solution, mainly because this code don't work if the class is defined in another directory.
Upvotes: 1