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.
Related
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.
I am really new to symfony but a long year developer. Now I decided to look at symfony but as it is with new things, problems aint far :)
I want to include a class into my project. Everytime I create a instanec of the class
$fw = new FloydWarshall($graph, $nodes);
i'll receive an internal error 500.
What am I doing wrong ?
Structure:
app/
...
bin/
...
src/AppBundle/
Controller/MyController.php
Entity/
...
Model/
fw.class.php
Reposetory/
...
var/
...
vendor/
...
web/
...
So in my class the namespace looks like
namespace AppBundle\Controller;
use AppBundle\Entity\lpNodes;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use AppBundle\Model\FloydWarshall;
....
In my fw.class.php the header looks like:
<?php
/**
* #package FloydWarshall
* #author Janne Mikkonen <janne dot mikkonen at julmajanne dot com>
* #date $Date: 2013/03/23 05:10:48 $
* #version $Revision: 1.1.1 $
* #license GNU General Public License, version 2 http://www.opensource.org/licenses/GPL-2.0
**/
namespace AppBundle\Model;
class FloydWarshall {
the constructor of FloydWarshall:
/**
* Constructor
* #param array $graph Graph matrice.
* #param array $nodenames Node names as an array.
*/
public function __construct($graph, $nodenames='') {
$this->weights = $graph;
$this->nodes = count($this->weights);
if ( ! empty($nodenames) && $this->nodes == count($nodenames) ) {
$this->nodenames = $nodenames;
}
$this->__floydwarshall();
}
As per comment, Symfony 3 uses either PSR-0 or PSR-4. Assuming that you're using Composer, you configure which one to support in your composer.json file.
With PSR-4, your filenames must correspond to your class names, and your directory names must match your namespaces. So if your entity is called FloydWarshall in the namespace AppBundle\Entity:
it must be in the directory src/AppBundle/Entity
the file must be called FloydWarshall.php
The reason for this strictness is the autoloader which comes with Composer. When you do new AppBundle\Entity\FloydWarshall, the autoloader knows where to find the file for that class. In your case, you had named it fw.class.php so the autoloader couldn't find it.
Tangentially to the question, are you using the dev environment? If you are, Symfony will give you a very helpful error message which will help you to diagnose the problem much quicker.
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.
what is the best practice for adding and accessing libraries to ZF? I'm trying to add phpmailer, should I just add it to the library folder? How can I access this file now, as it does not use the ZF naming conventions. Thanks!
Drop the relevant files into the library folder. Then you could create an autoloader something like this:
In the file library/My/Loader/Autoloader/PhpMailer.php:
class My_Loader_Autoloader_PhpMailer implements Zend_Loader_Autoloader_Interface
{
/**
* Autoload a class
*
* #param string $class
* #return mixed
* False [if unable to load $class]
* $class [if $class is successfully loaded]
*/
public function autoload($class)
{
if ('PHPMailer' === $class) {
include 'class.phpmailer.php';
return $class;
}
return false;
}
}
Then add this autoloader to the ZF autoloader stack, probably in Bootstrap:
protected function _initAutoloader()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
// The following is not necessary if My_ is already a registered namespace
// in the ZF autoloader
require 'My/Loader/Autoloader/PhpMailer.php';
$autoloader->pushAutoloader(new My_Loader_Autoloader_PhpMailer());
}
I've done something like this for examples with more involved legacy class naming/mapping and it's been quite helpful.
The simple solution is to drop the PHPMailer class (and any other files) into your library folder.
As PHPMailer seems to use a filename convention that won't apply well to the autoloader (class.phpmailer.php), I'd just require the class file in your Bootstrap
You should then be able to create a new PHPMailer class anywhere in your app.
You could try and register an autoloader that would map the class name PHPMailer to class.phpmailer.php however IMO it's not worth the effort.
UPDATE
Come to think of it, I'd ignore the Bootstrap part and simply require the class when it's needed (say, in your controller). As the file is in your include path, all you would need is
require_once 'class.phpmailer.php';
$mailer = new PHPMailer;
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');