How to load a class in symfony? - php

I need to load a class in Symfony 1.4 but the simple placing of the file in /apps/frontend/lib seems not to be enough.
class test
{ function foo ($foo) { echo $foo; }
}
and I tried to use it in an action:
public function executeTest(sfWebRequest $request)
{
$a = new test();
$a->foo('aaa');
}
I have refreshed the cache and still:
Fatal error: Class 'test' not found in ...
Do I have to declare it somewhere?

You have to place your file in your project lib folder:
myproject/lib
[Updating]
About loading a class from a file in myproject/apps/frontend/lib/ maybe the problem is the filename where is the class.
I found in this page A Gentle Introduction to symfony - Chapter 2 - Exploring Symfony's Code that:
Symfony will then look for a MyClass definition in all files ending
with class.php in one of the project's lib/ directories. If the class
definition is found, it will be included automatically.
So if you store all your classes in lib/ directories, you don't need
to include classes anymore. That's why symfony projects usually do not
contain any include_once or require_once statements.

Contrary to the other answer, classes in /apps/frontend/lib/ absolutely should be loaded by the autoloader if you are in a frontend action.
Something else is going on here. Is your class actually named test? If you're in production, have you cleared your cache?

Related

How does PHP's spl_autoload_register resolve circular dependencies with require_once?

How does PHP's spl_autoload_register resolve circular dependencies with require_once?
Circular dependencies can be resolved some cases, but not all. Let's start with an example of when it fails. Suppose we have three classes defined in separate files:
cat.php
class Cat extends Animal {}
animal.php
require_once('cat.php');
class Animal extends Creature {}
creature.php
class Creature {}
Let's say we also have a script that has an autoloader and creates an instance of Animal:
run.php
spl_autoload_register(function($className) {
require_once("$className.php");
});
$a = new Animal();
Running this script with "php run.php" will result in a PHP Fatal error:
PHP Fatal error: Class 'Animal' not found in .../Cat.php
I think this makes intuitive sense to me, because of the circular dependency between Animal and Cat:
The autoloader attempts to load animal.php
Loading animal.php causes cat.php to load due to the require_once()
Loading cat.php fails becasue it extends Animal, and Animal can't be loaded twice by the autoloader.
Here are some modifications to ensure that we don't get a fatal
animal.php should not have a require_once('cat.php')
This seems like the best solution as it effectively removes the circular dependency between Animal and Cat
Animal class should not extend Creature
Instead of using the Autoloader in run.php, just have a require_once() for both animal.php and creature.php
Questions:
Why does #2 work? Why does Animal not extending Creature result in the resolution of the circular dependency between Animal and Cat?
Why does #3 work? Isn't the autoloader just doing a require_once() under the hood?
The complete code (with some additional logging) from this examples can be found here
Since your autoloader does - what its name says - auto load your classes, you dont need any other require then the one in the autoloader function.
If you use require_once instead of require in it, it will still only load it once, no matter if you extending from it or just create an object.
So just use the code you posted in your question and remove the require_once() in your animal.php since the autoloader already requires it.
Side note: If you dont want to deal with creating your own autoloader, you could use the composer autoloader. Its easy to install and very useful, because it deals with sub directories and makes you follow a strict namespace convention.
If you want to do so, you need to install composer first. Then, you create a file called composer.json in your base directory with following content
{
"autoload": {
"psr-4": { "YourProject\\": "src/" }
}
}
You then need to execute following command in your command line:
cd path/to/your/project
composer dump-autoload
If you have done it put your classes in basedirectory/src
Note that you now have to give all classes a namespace, in this case if would be namespace YourProject. You are finally done!
Now go in your base directory and create a file, lets call it index.php:
require_once ('vendor/autoloader.php');
$a = new YourProject\Animal();
Sorry for long side note, sir!
Remove require_once('cat.php'); from animal.php
If an object does not exist, the autoloader is called making a separate call each object that does not exist. So if every object has its own file name you are alright.
The codeflow currently go's a little like this:
$a = new Animal();
#animal does not exist, spl_autoload('animal');
#include cat.php
#creature does not exist, spl_autoload('creature')
However, this go's by script path location. Meaning cat.php is perhaps not in the same directory as animal.php, thats why the file may not exist.
However, for a more dynamic autoloading system I recommend the the PSR-4 autoloader. It uses namespaces to determine the location of the class within the directory tree.
new \path\to\Animal();
Would cause the auto loader include the file of /path/to/animal.php
The reason your example is failing is that you're attempting to use the definition of Animal before it's defined.
First, you attempt to autoload Animal:
$a = new Animal(); // This triggers the require_once of animal.php
In animal.php, you require cat.php before you define the Animal class
require_once('cat.php'); // This file will be fully evaluated before the next line
class Animal extends Creature {} // Since the prior line relied on the existence of Animal, you get a compile error.
The Animal class can't be autoloaded inside cat.php, since you already called require_once on its containing file; it just hasn't fully been evaluated.
As others have said, this is a result of mixing direct require_once calls with an autoloader. As another workaround, you could move your require_once('cat.php') to the end of the file, and it should work as you're expecting it to.

Requiring autoload in multiple files

I have succesfully created an autoload function for a project I am working on.
I have several classes and 1 index where I instantiate all of the classes.
In some of the classes however I do need to require some other classes in order to let the project work.
My question being: How will it affect my project if I instead of requiring the few classes I need in the other classes require the autoload class?
The whole idea of autoloading classes is that you don't need to worry about this anymore. To achieve this, once you added your individual autoloading function, it will be used.
Say you have the following setup:
index.php
spl_autoload_register(function ($class) {
//something
});
$a = new Foo();
and the file where Foo is routed to
class Foo extends Bar {
public function __construct() {
$b = new Baz();
}
}
Since the autoloading was already invoked when searching for Foo, the same rules will now apply to find Bar and Baz.
This means once you defined your personal autoloading rules, PHP will go with them (until you redefine). This doesn't only work for inheritence but regular class calls inside your classes as well.

Codeigniter Custom Libraries and Namespaces

I have been creating custom libraries for a project that i've been porting over into the CI framework and I ran into an issue where certain classes have identical names.
To circumvent this issue, I tried implementing namespaces to no avail. I've been doing research and I know in the past this may not have been possible but with the newer version of PHP, I was wondering if there was a way to do this or if I was doing it correctly.
CI Version: 2.1.4
PHP Version: 5.4.12
Here is a demo of my setup:
application/libraries/class1.php
<?
class class1{
public function __construct()
{
$CI =& get_instance();
$CI->load->library('foo/class2.php');
}
}
?>
application/libraries/foo/class2.php
<?
namespace foo
class class2{
function bar(){
}
}
?>
When I run my CI application, I will get the following error:
Non-existent class: class2
Thanks for any help.
From what I've found, if the library file doesn't have
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
as the first line of code it won't load the library.
But then the namespace declaration needs to be above that.
CodeIgniter was written for PHP4 whereas namespace is PHP5.
There's more information in this thread: Namespace in PHP CodeIgniter Framework
Codeigniter does not support namespaces in 2.x and 3.x, what I would usually do especially with 3rd party libraries is load them in manually. Using your example, I would do:
// you can still manually load in libraries like this
require_once(APPPATH.'libraries/foo/class2.php');
class class1 {
public function __construct()
{
$CI =& get_instance();
// instantiate the class2 and make it available for all of class1 methods
$this->class2 = new foo/class2();
}
}
Just because it's a framework doesn't prevent you from using core php functionality, most people forget that you can still do normal php methods to achieve the same results.
This issue arose in a personal project while attempting to use some third-party libraries.
To get around it (without modifying the source files), I made a "bootstrap" class that loaded and extended the core library:
<?php
require_once(APPPATH.'libraries/foo/class2.php');
class class2 extends foo\class2 {}
This "bootstrap" class can then be loaded and used as if it were the extended one:
$this->load->library("class2");
$this->class2->bar(); // same as foo\class2->bar();
The issue is that 'load' doesn't take into account namespaces as far as I know.
which means that load('foo/class2') will look for the folder 'foo' inside the libraries folder.
you include the files normaly, when you create a new object you use the 'foo/bar'.
I'm not if the load class supports that though, You might need to simply create a new object manually(which is what the load class does anyway, it includes the file and creates a new object).
I'll quote an answer from Alex.
I know I've answered this question before I just can't find where and don't know what to search. So here it is: Codeigniter can only load single php file libraries (excluding drivers which is a different thing entirely). To load this kindof library (namespaced) you have to use something like: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md (class example).
Let's call it Autoloader_psr4 and save it in libraries (modify the class declaration to match this name verbatim (e.g. Autoloader_psr4). Remove the namespace declaration in the class so it looks like: https://pastebin.com/NU8Rbp7Y
Let's also move all the files in src/randomorg/ or src/foo to just be in a folder in third_party called RandomOrg or foo e.g. application/third_party/RandomOrg or application/third_party/foo. Your folder should look like the contents here: https://github.com/defiant/randomorg/tree/master/src/randomorg
Usage:
$this->load->library('autoloader_psr4');
$this->autoloader_psr4->register();
$this->autoloader_psr4->addNamespace('RandomOrg', APPPATH . 'third_party/RandomOrg');
$random = new \RandomOrg\Client(); // or whatever...

How to include a class in CakePHP 2.x

This is a very basic question but I can't figure it out.
I have a class that could be named myClass.php, this is a standalone PHP script that contains a standard object definition like this:
<?php
class myClass
{
public function myFunction($param1, $param2){
return $param1*$param2;
}
}
All I want to do is to be able to call to this class from a model, this is from App/Models/MyModel.php be able to simply do $myClass = new MyClass();
Where should I store myClass.php in the file structure and how can I make it visible for MyModel?
Thanks!
You'll note that you have a 'vendors' directory inside the root (ie, on the same level as the 'app' folder). This is for placing non-CakePHP related files that are shared across applications. You also have an 'app/Vendor' directory, which is for storing non-CakePHP files that are specific to that one app.
So, it should go in one of these two directories - most likely, in app/Vendor.
There's a little bit to know about loading vendor files - you can read the details here.
In your case, you probably just need:
App::uses('myClass', 'Vendor');

__autoload not respected when testing with PHPUnit

How can I make PHPUnit respect __autoload functions?
For example, I have these three files:
loader.php
function __autoload($name)
{
echo "foo\n";
require_once("$name.php");
}
test.php
require_once("loader.php");
class FooTest extends PHPUnit_Framework_TestCase
{
function testFoo()
{
new Foo();
}
}
foo.php
require_once("loader.php");
new Foo();
As expected php foo.php errors out, saying that file "Foo.php" doesn't
exist. The testFoo() function however errors out by saying that there is
no such class as Foo, and never echos the "foo\n" line.
This is expected behavior.
See this PHPUnit bugtracker entry: Upgrading to 3.5.10 has broken function of "autoload"
As of PHPUnit 3.5:
PHPUnit now uses an autoloader to load its classes. If the tested code requires an autoloader, use spl_autoload_register() to register it.
Quick fix:
The only change required is to add spl_autoload_register('__autoload') in your bootstrap script.
Longer fix:
If you can I'd suggest you just get rid of __autoload all together and use spl_autoload_register in your application as it is the way to go with PHP 5 code. (If you only have one autoloader you can't use the autoloader of your framework and so on)
Try using spl_autoload_register instead of __autoload. spl_autoload_register allows for multipe autoloaders to work together without clobbering each other.
PHPUnit uses spl_autoload_register which turns off __autoload.

Categories