namespaces and autoload - the right way - php

I want to use Facebook's PHP SDK and want to implement __autoload with it and I'm having problems.
At first I thought that this will work:
function __autoload($class) {
if(file_exists(APPPATH."libraries/facebook/".$class.EXT)) {
require_once(APPPATH."libraries/facebook/".$class.EXT);
}
}
The path is correct by the way.
Now I wanted to use:
$fb = new FacebookSession();
And it didn't work (unknown class), I then discovered this is FacebookSession.php:
namespace Facebook;
I'm not experienced with namespaces in PHP, but I tried this:
$fb = new Facebook\FacebookSession();
My autoloader stopped working, because now $class in __autoload is equal to "Facebook\FacebookSession"
How to handle this? I can't simply do explode or something similar on "\" because, I believe, there can be more such elements, right? Or maybe it's enough to do explode and take the last element, which fill be the final class name to look for? It does work, but is this the right way?

Related

make NAMESPACE declaration conditional?

I include myfile.php file 2 times.. I want to know if there is way to put in the start of myfile.php something like this:
if($authorized){
Namespace MyProject;
}
function ABC(){}
function EFG(){}
(I do this to avoid function-redefine errors. Please dont ask me why I do such thing.. I know there are if function_exists and etc.. but I need answers to what I ask).
update:
I do this, because I need to define function XYZ() and using that before other framework is loaded (which newly defines globally function XYZ()). So I want to use that function (with different functionality) before framework loads, and after framework loads, it should behave as framework decides.
Is there a way to make namespace's conditional?
Although this is impossible directly, I could suggest using the define() method to create this yourself. Where the file requiring the class needs to have a allowed definition to access the file.
define('IN_NAMESPACE', 0);
if(defined('IN_NAMESPACE')) {
// authorized
}
But if you're worrying about a class name being repeated, namespaces are for the declaration of environments so you do not get duplicates, for example:
namespace Environments\One;
class Example { }
namespace Environments\Two;
Class Example { }
use Environments\One\Example as ExampleOne;
use Environments\Two\Example as ExampleTwo;
$e_o = new ExampleOne();
$e_t = new ExampleTwo();
Or simply, directly say you will use this environment like so:
$e_o = new Environments\One\Example();
$e_t = new Environments\Two\Example();
But again, this issue is not canonical to PHP. Use MVC methodologies to over-come these issues and Singleton/Dependency Injection design patterns.

PHP Instance object with path

I'm trying to create an instance of an object with a path like(C:\wamp\www...)
In a new project I have this method, and I try with that to instance an object of an another project.
public function getControllerObject($class)
{
$object = null;
$class = realpath($class);
$class = str_replace('.php','',$class);
$object = new $class();
}
the variable $class have for exemple this value :
C:\wamp\www\myproject\projectBundle\Controller\DefaultController
But i get FatalErrorException: Error: Class. Class not found
I already try to put 2 backslash but it doesn't work.
Any idea?
Use basename, not realpath:
$class = basename($class);
For your example it should produce DefaultController.
First of all, what you are trying to do sounds like a horrible idea! If you want to access the class from another project you should add it (the project or the files) to your dependencies.
Your problem is, that what you basically do is:
$object = new C:\wamp\www\myproject\projectBundle\Controller\DefaultController();
which is obviously not what you want. When you do not enter a valid path (which I assume is what happened) realpath will return false and it won't work either.
You have to somehow determine which part of the path is part of the namespace and which is not. You could do this by also adding the project root, meaning which part to strip from the path or the namespace itself, i.e. the part to keep from the path (assuming you follow the recommendations from PSR-0), which should leave you with what you want (after str_replace() a preg_replace() or something similar).
Anyway, the cleanest way to solve your problem (apart from adding a dependency), is to use Symfony Class Loader instead of hardcoding paths into your application.

spl_autoload_register is not initializing autoload stack

I am trying to use the SwiftMailer php library with a program that I wrote. I have been using the spl_autoload_register() function just fine before including this library. However, prior to using this library I was explicitly defining the class extensions and locations using the spl functions:
set_include_path(get_include_path().[my include path]);
spl_autoload_extensions('.class.php');
spl_autoload_register();
session_start();
The problem I'm running into, is that now I'm trying to use a library that does not follow along the same naming conventions. Their own autoload class (built by the initial call to the library) looks like this.
public static function autoload($class)
{
//Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_'))
{
return;
}
$path = dirname(__FILE__).'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path))
{
return;
}
if (self::$initPath && !self::$initialized)
{
self::$initialized = true;
require self::$initPath;
}
require $path;
}
When I try to simply run the program after calling their class I get:
Fatal error: spl_autoload() [<a href='function.spl-autoload'>
function.spl-autoload</a>]:Class Swift_MailTransport could not
be loaded in [my file] on line 30
Line 30:
$transport = Swift_MailTransport::newInstance();
I have tried using a custom autoload class modeled after theirs, however, all I get when I try:
var_dump(spl_autoload_functions());
results:
bool(false);
I know this has to be a fairly simple issue, something that I'm overlooking, but I can't find it.
Any help would be greatly appreciated.
Try removing this:
spl_autoload_register();
From the documentation:
[if] spl_autoload_register() is called without any parameters then
[spl_autoload(...)] functions will be used
Knowing that, it's only logical to think that spl_autoload does not know where to load your SwiftMailer classes because the errors you get say so. It then follows that SwiftMailer is not in your include path because spl_autoload tries to load from there.
Next step is to put your SwiftMailer classes in one of the include paths.
Ok, after knocking my head against the wall all day and getting nowhere, I got a great piece of feedback from my brother who is also a programmer.
The whole problem, stemmed from this one line:
require_once(SITE_ROOT.'/classes/lib/swift_required.php');
The SITE_ROOT variable was actually referencing the web location (i.e. http://), with my current host, this does not work, it needs to use the physical file location instead. After making this change, the included autoloader works as advertised.

Am I required to use namespaces in Symfony2?

I've just begun learning Symfony2 (after using 1.x for the past 2 years) and am kind of put off by how much more typing is required. I know that sounds lazy, but I love the fact that I can quickly get something up and running in 1.x with much less typing. I'm wondering if it's possible to autoload classes without needing to use namespaces. All my attempts to do so (using the PEAR naming scheme) have failed.
If I'm missing something obvious and would be shooting myself in the foot by avoiding using namespaces, I'd appreciate any advice :)
In response to #KingCrunch:
I'd like to avoid the namespace and use declarations that seem to be used very frequently in Symfony2 simply to speed up my coding. To be honest, I haven't used namespaces in PHP before. I understand their benefit on paper (and I'm used to using packages in other languages) but I've never run into an issue by not using them in Symfony 1.x projects. This is why I made the "If I'm missing something..." statement above.
You have to realize that namespaces are not requiring you any more typing than PEAR-style names. Actually they can save up some characters.
See those two examples:
With PEAR-style:
class Foo_Bar_Baz extends Foo_Bar_Parent
{
public function __construct()
{
$obj = new Some_Long_Class_Name;
$obj2 = new Some_Long_Class_Name;
}
}
With namespaces/use:
namespace Foo\Bar;
use Some\Long\Class\Name;
class Baz extends Class
{
public function __construct()
{
$obj = new Name;
$obj2 = new Name;
}
}
With namespaces, but no use:
namespace Foo\Bar;
class Baz extends \Foo\Bar\Class
{
public function __construct()
{
$obj = new \Some\Long\Class\Name;
$obj2 = new \Some\Long\Class\Name;
}
}
As you see, if you use fully qualified class names every time (last example), you just have one more char per class name, the leading \. If you use the use statements and all, then it gets shorter the more you re-use the same class names in one file, or the more classes you use that are in the same namespace.
TL;DR: Anyway, if you're lazy, get an IDE like PhpStorm that will autocomplete all those and add the use statements for you.
Short answer: yes, namespaces are a must if you want to use Symfony2.
The reason behind this is that sf2 class autoloader is built on the namespace usage. In theory, you could write your own autoloader and wrap it around sf2, but I think this would be more hassle than using namespace and use ;)
At first I was also bummed by it and didn't really like the way it's used. But once I got used to it and started to see the benefits, it's the other way around. I use sf2 for my own projects and must use sf1.4 at work (not for long, hopefully) and every time I switch from sf2 to sf1.4 I get the "oh, not this again" feeling.
NS is so complex that you can even make a mistake when trying to explain the benefits to someone:
I know the point of the answer wasn't to get the code "perfect", but still...You forgot to include the use statement for the "Class" class:
namespace Foo\Bar;
use Some\Long\Class\Name;
use The\Extended\Class;
...
This is why namespaces are horrid - only takes forgetting 1 to throw an error; and, even if there is only 1 class named 'Class' in the entire project, PHP has no idea it's there.

Exception Based Class Loading in PHP

So I have an idea, but I'm thinking I need to run it by StackOverflow before I do something stupid.
I want to have an associative array of class names => filepaths. If PHP ever runs into a fatal error where the class is not defined, it will check if the key exists in my array and then require_once the class. This will prevent unnecessary bulk loading of classes that may never be used.
Bad idea?
How about trying PHP's built in autoloading.
Autoloading is the right way to do it, but spl_autoload_register is a cleaner way than __autoload, because it allows multiple autoloaders. Function __autoload also AFAIK stops working when spl_autoload_register is called, unless __autoload is also registered.
You can write your own autoload or use an existing one. For example, Zend Framework has an autoloader that uses conventions (Foo_Bar is in Foo/Bar.php). Nette Framework has RobotLoader, that indexes your classes and uses the index when neccessary. However, unless you use other things from the framework, it is probably too large.
see: http://www.php.net/manual/en/function.spl-autoload-register.php
If you are on PHP5, you can use __autoload().
makes your code a bit more manageable , although performance-wise, it's a bad choice. But I wouldn't worry it unless I'm building a Facebook.
What you are trying to do is already handled by the php __autoload function. You can read all about it here: http://php.net/manual/en/language.oop5.autoload.php
So, not a bad idea at all ;)
you should use autoloading with specified clas name structure, here is an example
the class names should should be only alpha and _ case-insensitive.
Lets take this directory structure and files
/classes/class.php
/classes/input/input.php
/classes/output/output.php
/classes/regex/regex.php
/classes/interface/parser/interface_parser.php
/classes/parser/parser.php
/classes/parser/xml/parser_xml.php
/classes/parser/html/parser_html.php
having the structure like this is good as it encourages you to code better when it comes to OOP.
Now if we take a look at the /classes/parser/html/html_parser.php file:
class Parser_Html extends Parser implements Interface_Parser
{
//looks nice in here
}
usually you would have to make sure the interface and the extended class is loaded, but these get autoloaded as well if they have not already.
creating the auto load system for this is not that complex, its just 1 function.
function __autoload($name)
{
//Classes
$parts = explode('_',strtolower($name));
$path = '/classes/';
foreach($parts as $p)
{
$path .= $p;
}
$path .= '/' . $name . '.php';
if(file_exists($path))
{
require_once $path;
}
}
so instead of including the class file first just run the class initiation.
$HtmlParser = new Parser_Html();
as the file has not been include the __autoload is run with a param of the class name, the autoload then looks in the directory that's relevant to the class name to try and load it.
also as your using the extend keyword in the class file shown above the class that is to be the parent gets run threw trhe autoloader aswell so you do not need to pre-load interfaces and classes etc.
Hope this helps you.
Note:
All code provided is untested and written for informational purposes, I would recommend you research the techniques more in detail before any implementation is done.

Categories