__DIR__ VS using Reflection - php

In Symfony2, I saw the code like below:
if (null === $this->rootDir) {
$r = new \ReflectionObject($this);
$this->rootDir = dirname($r->getFileName());
}
why not just use the __DIR__?
if (null === $this->rootDir) {
$this->rootDir = __DIR__;
}
What is difference between them?

__DIR__ returns the directory of the file where it is called. The Symphony2 code returns the directory of where the class is defined, which most likely is a different file.

As PHP manual states:
DIR returns the directory of the file. If used inside an include, the directory of the included file is returned
FILE returns the full path and filename of the file. If used inside an include, the name of the included file is returned.
So these constants always returns paths of the file where there are used. However, I suppose that it is not the expected behaviour in the code snippet you cited. Possibly the code resides in some base class, while it can be invoked from extending classes. If we want to get the path to the current class, the first way is the correct one.

__DIR__ exists only in PHP 5.3. Before 5.3, we had to use dirname(__FILE__) or something similar.

I think it is because __DIR__ will return the directory of the script that was initially invoked. In the code example, you would get the directory of the object's class. I may be wrong though have not tried this yet please correct me anyone if I am.

Related

Problems with spl_autoload_register()

I do have a problem with one of my php application. First of all my test environment is running on IIS with PHP installed. There I have the following code:
<?php
// define autoloader for all php classes
$ps = PATH_SEPARATOR; // add default path seperator to a variable
set_include_path(get_include_path().$ps."classes/adapter/".$ps."classes/common/".$ps."classes/manager/".$ps."classes/mapper/".$ps."classes/object/".$ps."classes/viewobject/"); // add classes directory to include paths
spl_autoload_extensions(".class.php"); // add .class.php as extension for autoloading classes
spl_autoload_register(); //start the autoloading procedure
?>
So far so good, since this is working as it should. Now I wanted to move that page to my provider which uses Apache with PHP and what I see there is the following error message:
Fatal error: spl_autoload() [<a href='function.spl-autoload'>function.spl-autoload</a>]: Class pageLoader could not be loaded
Do you guys may have any idea what I am doing wrong?
Thanks a lot for your help.
looks like I found the issue.. I was aware that linux is case-sensitive, but for any reason the filenames of the classes have to be in small letters.
In my example if you make a call
$test = new pageLoader();
The file in the directory needs to have the name "pageloader.class.php"
Cheers
Did you forget to define the loader function?
// Autoloader for Classes
spl_autoload_register(null, false); // Nullify any existing autoloads
spl_autoload_extensions('.php, .class.php'); // Specify extensions that may be loaded
function class_loader($class) // Class loader
{
$filename = strtolower($class) .'.class.php';
$file = ROOT .'/classes/'. $filename;
if(!is_readable($file))
return false;
require_once $file;
}
spl_autoload_register('class_loader'); // Register loader function(s)
// End Autoloader
Note the code above assumes a defined constant ROOT

How to import all classes within filesystem in php?

The folder structure is like so:
/main.php
/lib/class1.php
/lib/class2.php
/lib/class3.php
I want to have main.php make available all the classes in lib without doing a ton of require/include. Is this possible? Is it possible to just include all files within a directory?
Create an autoloading function to load the class directly from your URL
function __autoload($class_name) {
include "/lib/".$class_name . '.php'; //Add your folder structure like this
// ^ Change the path to your specific need
}
//Then Simply
$class1_object = new Class1();
Yes, yes it is entirely possible and quite easy to do. Best way to do so would be to utilize http://uk1.php.net/autoload
I would suggest you use an autoload function like #Puciek suggested.
If you are curious about doing it yourself though, you can do something like:
$path = "lib/";
foreach(new \DirectoryIterator($path) as $lib){
if(strstr($lib->getFilename(), ".php")){
require_once $path.$lib->getFilename();
}
}
The above will use DirectoryIterator which is part of the PHP SPL.
$path = '/lib/';
$dir = new DirectoryIterator($path);
foreach($dir as $spl){
if($spl->getExtension() != 'php') continue;
include($spl);
}
Basically I looped through the directory's files, filtered out the not php files, then included them. You can feel free to add more filters like files starting with "class" as filenames.
You can also have a look to glob() function to php, which returns an array with all the files within a directory:
http://it2.php.net/manual/en/function.glob.php
The good thing about the directory iterator is that you can use a RecursiveDirectoryIterator alongside with a RecursiveIteratorIterator to iterate through a directory tree in a similar way if you want, to get all the files as well.
Autoloading would be a good option for this but that assumes there is a way to map class names to file names, if that is an option you should follow #puciek advise.
Other options would be to create a single include that includes all of the files you want for example in /lib create a file called all.php and define it as
include("class1.php");
include("class2.php");
include("class3.php");
then in your script you could just include("lib/all.php");
Another option would be to glob the files in a directory then loop through the resultant array and issue an include.
$includes = glob("lib/*.php");
foreach($inclues as $inc){
include($inc);
}
Use lazy loading rather than importing everything - http://php.net/manual/en/function.spl-autoload-register.php
function autoload($sClass) {
$sPath = str_replace('_', DIRECTORY_SEPARATOR, $sClass) . '.php';
require_once($sPath);
}
spl_autoload_register('autoload');
new My_Class();
This is a very stripped back example but it's something to build on. Ideally you would check that the file exists on the include path (http://php.net/manual/en/function.stream-resolve-include-path.php), check that the file is readable, that the require/include was successful and that the class exists at the end of the autoload

Test whether a file exists anywhere in the include path

I'm writing an autoload function and in the inner logic of it would like to test whether a certain file exists somewhere in the path prior to including it.
This is the logic:
If a file named $className'.specialversion.php' exists anywhere in the include path include it. Otherwise, let other autoloaders take care of including a file for this class.
At the moment I just do: #include($calculatedPath);
I'm not sure if it's a good approach to include and suppress the error. I would rather check if the file exists (somewhere in the include path) prior to including it.
My question is:
Can I test for existence of a file anywhere in the include path?
Is it really problematic to do #include($calculatedPath);?
Edit
An important accent: I don't know where the file should be. I just want to know whether it exists in one of the directories in the include path. So I can't just do file_exists() or something like that.
As of PHP 5.3.2 there is the option to use the stream_resolve_include_path() function whose purpose is to
Resolve [a] filename against the include path according to the same rules as fopen()/include() does.
If the file exists on one of the include paths, then that path (including the file name) will be returned. Otherwise (i.e. the file was not on any of the include paths) it will return FALSE.
Relating this to your needs, your autoloader might look something like:
function my_autoloader($classname) {
$found = stream_resolve_include_path($classname . '.specialversion.php');
if ($found !== FALSE) {
include $found;
}
}
You should avoid the error supressor operator #.
function autoload($class) {
// Build path (here is an example).
$path = DIR_CLASSES .
strtollower(str_replace('_', DIRECTORY_SEPARATOR, $class)) .
'.class.php';
if (file_exists($path)) {
include $path;
}
}
spl_autoload_register('autoload');
$front = new Controller_Front;
// Loads "application/classes/controller/front.class.php" for example.
Update
An important accent: I don't know where the file should be, I just want to know whether it exists in one of the directories in the include path. So I can't just do file_exists or something like this
If your class could be in a number of directories, you could...
Have your autoload function traverse them all, looking for the class. I would not recommend this.
Rename your classes to have a name that easily maps to a file path, like in the example code above.
If you do decide to traverse all folders looking for the class, and it becomes a bottleneck (benchmark it), you could benefit from caching the class name to file location mapping.
I would use file_exists rather than a warnings-suppressed include.
Then you'll have to iterate through the include_path:
$paths = explode(';', get_include_path());
foreach($paths as $p){
if(file_exists($p . '/' . $calculatedPath)){
include $p . '/' . $calculatedPath;
break;
}
}
As a simple resolution, you should a test within the SPL-function file_get_contents() by setting the second argument to TRUE.
--Rolf
I've written a function that can test it nicely
function fileExists($file) {
if(function_exists('stream_resolve_include_path'))
return stream_resolve_include_path($file);
else {
$include_path = explode(PATH_SEPARATOR, get_include_path());
foreach($include_path as $path)
if(file_exists($path.DS.$file))
return true;
return false;
}
}

include path and the __autoload function in php

I am trying to convert several php scripts to use the __autoload function. Right now I can use the include and require functions like this:
require_once('path/to/script.php');
But inside of the __autoload function, I can't use the line above. I have to use this:
require_once('absolute/path/to/script.php');
Why does it seem as though the __autoload function doesn't use the include path I have specified in php.ini?
Don't use __autoload... It has a few drawbacks (including limiting yourself to one per execution). Use instead spl_autoload_register if you're on 5.2+.
So what I typically do, is have a class:
class AutoLoader {
protected static $paths = array(
PATH_TO_LIBRARIES,
);
public static function addPath($path) {
$path = realpath($path);
if ($path) {
self::$paths[] = $path;
}
}
public static function load($class) {
$classPath = $class; // Do whatever logic here
foreach (self::$paths as $path) {
if (is_file($path . $classPath)) {
require_once $path . $classPath;
return;
}
}
}
}
spl_autoload_register(array('AutoLoader', 'load'));
That way, if you add a library set, you can just "add it" to your paths by calling AutoLoader::AddPath($path);. This makes testing with your autoloader a LOT easier (IMHO).
One other note. Don't throw exceptions from the autoload class unless absolutely necessary. The reason is that you can install multiple autoloaders, so if you don't know how to load the file, another one may exist to load it. But if you throw an exception, it'll skip the other one...
Personally, I don't ever like to use relative paths with includes. Especially with multiple include directories (like pear), it makes it very difficult to know exactly which file is being imported when you see require 'foo/bar.php';. I prefer to define the absolute path in the beginning of the file set define('PATH_ROOT', dirname(__FILE__));, and then define all my other useful paths off of that directory (PATH_LIBRARIES, PATH_TEMPLATES, etc...). That way, everything is absolutely defined... And no need to deal with relative paths (like the issue you're having now)...
I suspect your __autoload() function is in a separate file then the code which calls it. The path to the included files will be relative to the file which the __autoload() function declaration resides.
It seems like . is not in your include path. So add it use:
set_include_path('.' . PATH_SEPARATOR . get_include_path());
Now PHP should look relative to the executed scripts directory, too. (Executed script here is something like index.php, not autoload.php.
But why don't use simply use a normal relative path like ./path/to/class.php?
Not sure without seeing the whole set-up. My autoload function is within my global functions file, and looks like this:
function __autoload($class) {
if (file_exists("includes/{$class}.php")) {
require_once("includes/{$class}.php");
}
/**
* Add any additional directories to search in within an else if statement here
*/
else {
// handle error gracefully
}
}
I use a relative path because the script is included in my index.php file and all HTTP requests are passed through it.

Can I get the path of the PHP file originally called within an included file?

Let's say we have index.php and it is stored in /home/user/public/www and index.php calls the class Foo->bar() from the file inc/app/Foo.class.php.
I'd like the bar function in the Foo class to get a hold of the path /home/user/public/www in this instance — I don't want to use a global variable, pass a variable, etc.
You can use debug_backtrace to look at the calling path and get the file calling this function.
A short example:
class Foo {
function bar() {
$trace = debug_backtrace();
echo "calling file was ".$trace[0]['file']."\n";
}
}
Wouldn't this get you the directory of the running script more easily?
$dir=dirname($_SERVER["SCRIPT_FILENAME"])
getcwd() gets the current working directory
It can be changed for a variety of reasons by 3rd party modules, includes or even your own code by issuing a chdir().
debug_backtrace() as Devon suggested is the answer you're looking for.
Found it. getcwd().

Categories