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.
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.
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.
Related
I'm having some hard time wrapping around namespaces in PHP, especially when you code needs to interact with scripts residing in another namespace. I downloaded a Shopify API toolkit and trying to get it working. Everything was fine before I started adding namespaces to my code (which is required or threre is script collisition with other Wordpress plugins on my site). Also, the weird namespace {} bit at the top is because in this same file I want a globally accessible function for making the class a singleton.
Looking forward to learning more about how this works.
#### FILE BEING CALLED
namespace {
function SomeFunctionToBeAccessedGlobally() {
return 'Hello';
}
}
namespace MySpecialApp {
class ShopifyImport {
public function __construct() {
// Do Whatever
$this->doImport();
}
public function doImport() {
require __DIR__ . '/vendor/autoload.php';
$credential = new Shopify\PrivateAppCredential('standard_api_key', 'secret_api_key', 'shared_api_key');
$client = new Shopify\Client($credential, 'shop_url', [ 'metaCacheDir' => './tmp' ]);
}
}
}
#### FILE '/vendor/autoload.php'
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit73503f8de5d68cdd40a9c0dfd8a25b44::getLoader();
I do noice that some of the files which where part of the repository cloned into vendor have namespace Slince\Shopify; declarations. I tried to do a use with that namespace within my original namespace but it also didn't work.
The PHP Error being reported is:
Fatal error: Uncaught Error: Class
'MySpecialApp\Shopify\PrivateAppCredential' not found in
/.../ShopifyImporter.php:139 Stack trace: #0 (Blah Blah Blah)
Your code tries to create a new Shopify\PrivateAppCredential() object in the current namespace. However, this class does not exist in your namespace since it is part of the "vendor" namespace.
You can "reset" (read fallback) to the global namespace for your object creations by adding a \ in front of them, as described in the documentation:
$credential = new \Shopify\PrivateAppCredential('standard_api_key', 'secret_api_key', 'shared_api_key');
You can check the difference here without \ and with \.
I have following problem: I have class Router (in project/connection/api/callbacks) and TestRouter (in project/tests/api).
Class Router is only Example and I don't want psr-0 or 4.
Router has this code on the beginning:
<?php
namespace Connection\Api\Callbacks;
class Router
{
Test class start with this code:
<?php
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->add('Connection\\Api\\Callbacks', __DIR__ . '/../../connection/api/callbacks');
class TestRouter extends PHPUnit_Framework_TestCase
{
function test() {
$variable = new \Connection\Api\Callbacks\Router();
}
Then I got error class not found. Please where is the problem?
You don't want PSR-0, but you are using exactly that function. The code comment for the add() method you are using to add your class to the autoloader:
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* #param string $prefix The prefix
* #param array|string $paths The PSR-0 root directories
* #param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
I wonder what keeps you from simply using PSR-4 in this case. Just add the necessary declaration to your composer.json file.
When it comes to PHPUnit, add require "vendor/autoload.php" to the bootstrap file or use it as the full bootstrap file if you don't have to do anything else there. It will make writing tests easier because you don't have to take care of the autoloading and adding the individual class to the autoloader. Also, you won't end up instantiating multiple autoloaders that don't get removed from the autoloader stack.
I'm currently building an application that will require a few different name spaces, however most of the application I want to put under the app namespace.
In my index.php file I declared to use the app namespace, however PHP still insists my class cannot be found like so...
<?php
define('PATH',strtok ( $_SERVER['REQUEST_URI'] , '?' ));
define("ROOT",$_SERVER['DOCUMENT_ROOT'].'/',true);
require 'vendor/autoload.php';
require 'app/class_loader.php';
use Carbon\Carbon;
use app\routing;
$routing = new routing;
$routing->router();
and inside my routing class I declare it as part of the app namespace like this
<?php
namespace app;
class routing
{
However I keep getting Fatal error: Class 'app\routing' not found in /var/www/my_app/index.php on line 15
This is my autoload file
<?php
function autoload_class_multiple_directory($class_name)
{
# List all the class directories in the array.
$array_paths = array(
'app/controllers',
'app/models',
'app/services',
);
foreach($array_paths as $path)
{
$file = $path.'/'.$class_name.'.php';
if(is_file($file))
{
include $file;
}
}
}
spl_autoload_register('autoload_class_multiple_directory');
I thought this is how namespaces worked? I want to ensure I make my application as easy to follow and code in from the get-go, could somebody please help point me in the right direction?
cheers!
spl_autoload_register('Think\Think::autoload');
Under namespace Think\ I created the above register function,when I try to use a class that has not been included like class Storeage,php will surposely pass Storeage as the variable to function Think\Think::autoload,but it actually passed Think\Storeage as the variable,why it adds the extra Think\ to the autoload instead of just Storeage?
Does that mean autoload will only search for classes which are declared under the same namespace where the autoload function is created?
Autoload functions generally work by including files for you on demand. So, for instance, I have a class called Spell in the namespace Write and it's in write/spell.php. So I tell my autoload function how to find the file (in this case, my directories mirror my namespacing).
The autoload function itself doesn't care about namespaces per se. It cares about finding the files that contain your class and loading them. So, to answer your question, your autoload will only restrict itself to a namespace if you write the function to do that.
Now, here's the caveat with the way you're doing it. Your autoload function is already in a namespace. That means you will have to manually include the file that contains that class or else your autoload will fail.
Here is an example for you .
loader.php
namespace bigpaulie\loader;
class Loader {
/**
* DIRECTORY_SEPARATOR constatnt is predefined in PHP
* and it's different for each OS
* Windows : \
* Linux : /
*/
public static function load($namespace){
$filename = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . ".php";
if(file_exists($filename)){
require_once $filename;
}else{
throw new \Exception("Error Processing Request", 1);
}
}
}
index.php
require_once 'path/to/loader.php';
spl_autoload_register(__NAMESPACE__ . 'bigpaulie\loader\Loader::load');
$class1 = new \demos\Class1();
// or
use bigpaulie\core\Class2;
$class2 = new Class2();
as you can see we can use whatever namespace needed we just have to make sure that the path to the class file exists .
Hope this helps!
Best regards,
Paul.
I have a folder in my library folder which is named after my website. The folder path is like:
~\www\library\myWebsite.com
If I'm using Zend autoloader to load the namespace of everything in the library path, will I have any trouble autoloading a class from that file with a namespace like this:
\myWebsite.com\myClass::myFunction();
I have looked online for documentation on this and I can't find any info about using periods in this way.
I tried it and the complication is in PHP. I think Zend is registering the namespace fine, because when I call \Zend_Load_Autoloader::getRegisteredNamespaces() it shows that it's registered. but when I call the static method from the fully qualified namespace, php gives an error of this:
Fatal error: Undefined constant 'myWebsite' in /home/jesse/www/application/controllers/MyController.php on line 15
It seems like PHP is terminating the namespace identifier, during parsing, at the . (period character). This is dissapointing because to me having a library named after the website was important to my design.
I will rename the directory to myWebsitecom or possibly make the .com it's own sub directory like
myWebsite\com and incorporate that into my namespace tree like: \MyNamespace\Com\MyClass::myFunction();
The easiest way to find out is to try it.
If it doesn't work, you could always write a custom autoloader to make it work. I don't have much experience with php namespaces, but the autoloader would look something like this (I imagine you'll have to tinker with it a bit to determine the correct file path given the class name):
<?php
class My_Loader_Autoloader_MyWebsite implements Zend_Loader_Autoloader_Interface {
/**
* (non-PHPdoc)
* #see Zend_Loader_Autoloader_Interface::autoload()
*/
public function autoload($class) {
if (strtolower(substr($class, 0, 9)) == 'mywebsite') {
$file = realpath(APPLICATION_PATH . '/../library/myWebsite.com/' . $class);
if ($file) {
require_once $file;
return $class;
}
}
return false;
}
}
then put this in your bootstrap:
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader(new My_Loader_Autoloader_MyWebsite());
and if this class must be in that myWebsite.com directory, you could just cheat and throw in a require in there too:
require_once(APPLICATION_PATH . '/../library/myWebsite.com/Loader/Autoloader/MyWebsite.php');