Just a quick question on your preference of using PHP namespaces or class prefixing.
<?php
// ----- namespace -----
use My\Namespace;
$object = new Namespace\Object;
// ----- prefix with PR -----
$object = new PF_Object; //or
$object = new PFObject;
Which do developers prefer? I know why the use of namespaces can bring great advantage to applications, but also can quite a hindrance in PHP in my own opinion.
Thanks!
Combine use with an alias:
use My\Namespace\Foo as Foo;
$object = new Foo;
It's also useful like so:
namespace My\Debug\Stuff;
use My\Production\Stuff\Foo as BaseFoo;
class Foo extends BaseFoo {}
Why would you consider namespaces a hindrance?
class prefixing seemed to me like a sort of a hack to implement 'in code' a mecanism to implement namespaces.
Newer code now has the option to use a native built-in way of handling namespaces. This is a much cleaner approach, in my very humble opinion.
Consider this popular yet eye-opening example that allows legacy code to use namespace:
// Using native namespace features to shorten class prefixes
<?php
use Sabre_DAV_Auth_Backend_PDO as AuthBackend;
use Zend_Controller_Action_Helper_AutoComplete_Abstract as AutoComplete;
$backend = new AuthBackend();
?>
Clearly, there's no need to use the underscore character to fake namespaces any more, so I suppose the question boils down to "is it a good idea to use underscore characters in class names".
IMO, the answer is no, firstly because it looks ugly (IMO) and secondly because some older class loaders could get confused by a class name that includes an underscore.
well
if you are running PHP 5.2.X you only have the option 2
but if you are running PHP 5.3.Xyou could use both.
In my case running PHP 5.3.X I would use the feature that the new version of the language offers. To make an analogy is likely be running JAVA 1.6 and don't use generics (or sort of)
Related
I've got a few PHP5.2- frameworks using long classnames like such:
class Product_Sub_SomeClass {
...
}
I'd like to be able to provide a way for PHP5.3+ users to use namespaces and short class names whilst retaining PHP5.2 compatibility of the framework.
The above example class should be available in PHP5.3 developers' code as:
use Product\Sub;
$someclass = new SomeClass();
But the exact same class (same sourcecode) should also be usable to PHP5.2 users as:
$someclass = new Product_Sub_SomeClass();
Can I just provide an additional file that handles all the aliasses or is there some other or better mechanism available?
What is best practice to provide namespaces and class aliasses to PHP5.3+ users of a PHP5.2- framework?
Found my own answer to this problem:
class Product_Sub_SomeClass {
...
}
// Alternative PHP 5.3+ namespace\class
if (function_exists('class_alias')) {
class_alias('Product_Sub_SomeClass', 'Product\Sub\SomeClass');
}
I have studied the use of Namespaces in PHP a while back but recently looking at a project that used the use keyword and then accessed the namespaced object as if they were normal without namespace.
My question is, is the code below correct, it hs a file index.php and uses the namespace MyLibrary\Base it then uses use to bring in \MyLibrary\Registry \MyLibrary\User and \MyLibrary\Request
It then can access any of these object without putting there namespace in front of them, so the actual code below the use section looks like a normal pre-namespace php file.
I am asking if this is how you use namespaces? Or am I missing something?
File: index.php
<?php
namespace MyLibrary\Base;
use \MyLibrary\Registry;
use \MyLibrary\User;
use \MyLibrary\Request;
class Base
{
public $registry;
function __construct($registry)
{
$this->registry = $registry;
$this->user = New User;
$this->request = new Request;
# code...
}
}
?>
File: registry.class.php
<?php
namespace MyLibrary\Registry;
class Registry
{
public $user;
function __construct($user)
{
$this->user = $user;
# code...
}
}
?>
Yes. The use-statement imports the class- or namespace-name into the current scope. To write everything that short is the reason, why the PHP-devs implemented namespaces ;)
namespace MyFirstNamespace {
class Foo {}
}
namespace MySecondNamespace {
use \MyFirstNamespace\Foo as Bar;
$foo = new Bar;
}
a) it make everything more readable, because its much shorter, than Vendor_Package_Foo_Bar_XyzClass and b) you can exchange the classes to use very fast.
# use \MyFirstNamespace\Foo as Bar; // I don't like Foo anymore
use \MyFirstNamespace\SimilarToFoo as Bar;
Namespacing has a lot of advantages to it.
The first on is you can reuse method names and even class names if it makes sense provided they exist within a different namespace. Example:
namespace \myNamespace\data\postgres;
class DataBase extends \PDO
{
}
namespace \myNamespace\data\mysql;
class DataBase extends \PDO
{
}
You could even reuse names that are normally reserved for PHP functions
namespace \myNamespace\dir;
function makedir ()
{
if (// some condition is true)
{
\makedir ();
}
}
All of this is intended to make it easier to use code from different sources together without having to worry about naming conflicts. Provided programmers are courteous enough to observe a few simple rules then the chances of name conflicts are hugely reduced. Actually, pretty much the only rule you need to concern yourself with to avoid naming conflicts is to make the first level of your namespace your own. For example, use your company name, or some other way of identifying you as a unique vendor as the first level of your namespace and everything should be good.
For example I use \gordian as the root namespace in all the code I write, as I can then call my classes under that namespace anything I like without worrying about them colliding with someone who chose a different root namespace.
So what's wrong with PEAR conventions, you might ask? A lot of projects follow them, including the popular Zend framework.
The answer is names become very unwieldy very quickly. For instance, Zend, as it follows the PEAR convention, uses a sort of pseudo-namespacing. All the classes in the collection start with Zend_ and with each level of class hierachy add a further part to the name.
Ze
As a result, you end up with class names like Zend_Db_Adaptor_Abstract and Zend_Dojo_Form_Decorator_TabContainer.
Should Zend update their framework to use namespaces (which I'm told is happening with Zend Framework 2.0) then they'd be replaced with \Zend\Db\Adaptor\Abstract and \Zend\Dojo\Form\Decorator\TabContainer instead. So what, you might ask? The answer is that you can alias them to much shorter names with the Use keyword, as you've already seen. This means you don't have to keep writing the full class name out, but only as far as to what you've aliased.
use \Zend\Dojo\Forn\Decorator as Dec;
$a = new Dec\TabContainer; // Not easy to do without namespaces!
Further more, if you're already in a given namespace, then you don't even have to use the use keyword to access other items within the same namespace by a short name, as it happens automatically for you in that case. For framework writers this is a huge timesaver.
For example, you might see something like the followign in Zend Framework 2 (as I'm not working on it in any way, this is purely an example and not from the actual ZF2 source).
namespace \Zend\Db\Adaptor;
class Postgres extends Abstract // We don't need to use \Zend\Db\Adaptor\Abstract here because it's in the same namespace already anyway
{
}
There are other benefits too, such as it makes autoloaders ridiculously simple to make (provided your namespace structure maps exactly onto your filesystem directory structure).
Namespaces can seem like one of those features that aren't really very important, or don't even seem to make any sense, but after using them for a little while their usefulness will suddenly become very obvious.
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.
Prior to PHP 5.3 I used to name interfaces/abstract classes like this:
abstract class Framework_Package_Subpackage_Abstract {}
Framework/Package/Subpackage/Abstract.php
interface Framework_Package_Subpackage_Interface {}
Framework/Package/Subpackage/Interface.php
Now with PHP 5.3 and using namespaces I can't use my convention anymore, because interface and abstract are reserved keywords.
namespace Framework\Package\Subpackage;
abstract class Abstract {}
Framework/Package/Subpackage/Abstract.php
namespace Framework\Package\Subpackage;
interface Interface {}
Framework/Package/Subpackage/Interface.php
So, what should I name my classes/interfaces like?
A current coding guide "PSR-2" basically suggests you drop the interface down one directory and combine the name.
eg:
File \Vendor\Foo\Interface.php ===> \Vendor\FooInterface.php.
and the use stament eg:
use \Vendor\Foo\Interface; ===> use\Vendor\FooInterface;
see: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
About this question (Abstract and Interface), you might have a look at the post "Migrating OOP Libraries and Frameworks to PHP 5.3" on Matthew Weier O'Phinney's blog -- it's about Zend Framework, and how they could solve that problem in 2.0.
One of the things they note is :
In other OOP languages, such as
Python, C#, interfaces are denoted by
prefixing the interface with a capital
'I'; in the example above, we would
then have Zend::View::IView.
So, in your example, you would have something like this, I guess :
namespace Framework\Package\Subpackage;
abstract class ASubpackage {}
Framework/Package/Subpackage/ASubpackage.php
namespace Framework\Package\Subpackage;
interface ISubpackage {}
Framework/Package/Subpackage/ISubpackage.php
What do you think about that ?
(I have not tested this way, but it doesn't look like a bad idea ? )
SubpackageAbstract,
SubpackageInterface
I personally would recommend avoiding any usage of Hungarian Notation, and consider following the Java standard for interface names; that is, they're named descriptively just like any other class. See this SO question for a discussion of the ins and outs of Hungarian notation.
A good example of using generic, descriptive names indicative of functionality or behavior can be found in PHP's own SPL, like: "Countable", "Iterator", "ArrayObject".
You could also do something like this:
src/Chess/Piece.php
<?php
namespace \Chess;
abstract class Piece implements \Chess\PieceInterface {}
src/Chess/PieceInterface.php:
<?php
namespace \Chess;
interface PieceInterface {}
src/Chess/Piece/Pawn.php:
<?php
namespace \Chess\Piece;
class Pawn extends \Chess\Piece {}
Here's how I would setup autoloading in composer.json
{
"autoload": {
"psr-0": {
"": "src/"
}
}
}
Honestly, I believe the Hungarian notation was introduced with C#, because there is not "extends" and "implements" keyword like in Java. So to differentiate the convention became to call it IView. In Java, the interface would be called View alone, and implementations would be called DefaultViewImpl, SmartyViewImpl or something like that. Since, PHP does have extends and implements keywords, it makes sense to use the Java convention.
I have heard the argument that the Hungarian notation does lend it self to making the API elements identifiable just by looking at the class names. In that case I would call it either IView or AbstractView.
In my opinion, the best way to solve this, is by simply appending Class to your classnames.
namespace Framework\Package\Subpackage;
abstract class AbstractClass {}
Framework/Package/Subpackage/AbstractClass.php
namespace Framework\Package\Subpackage;
interface InterfaceClass {}
Framework/Package/Subpackage/InterfaceClass.php
note that this is still not perfect (however, works perfectly) but i keeps the code similar to the original idea ;)
I'm maintaining library written for PHP 5.2 and I'd like to create PHP 5.3-namespaced version of it. However, I'd also keep non-namespaced version up to date until PHP 5.3 becomes so old, that even Debian stable ships it ;)
I've got rather clean code, about 80 classes following Project_Directory_Filename naming scheme (I'd change them to \Project\Directory\Filename of course) and only few functions and constants (also prefixed with project name).
Question is: what's the best way to develop namespaced and non-namespaced versions in parallel?
Should I just create fork in repository and keep merging changes between branches? Are there cases where backslash-sprinkled code becomes hard to merge?
Should I write script that converts 5.2 version to 5.3 or vice-versa? Should I use PHP tokenizer? sed? C preprocessor?
Is there a better way to use namespaces where available and keep backwards compatibility with older PHP?
Update: Decided against use of namespaces after all.
I don't think preprocessing the 5.3 code this is a great idea. If your code is functionally identical in both PHP 5.2 and 5.3 with the exception of using namespaces, instead of underscore-separated prefixes, why use namespaces at all? In that case it sounds to me like you want to use namespaces, for the sake of using namespaces..
I do think you'll find that as you migrate to namespaces, you will start to 'think a bit differently' about organizing your code.
For this reason, I strongly agree with your first solution. Create a fork and do backports of features and bugfixes.
Good luck!
This is a followup to my previous answer:
The namespace simulation code got quite stable. I already can get symfony2 to work (some problems still, but basically). Though there is still some stuff missing like variable namespace resolution for all cases apart from new $class.
Now I wrote a script which will iterate recursively through a directory and process all files: http://github.com/nikic/prephp/blob/master/prephp/namespacePortR.php
Usage Instructions
Requirements for your code to work
Your classnames mustn't contain the _ character. If they do, classnames could get ambiguous while converting.
Your code mustn't redeclare any global functions or constants within a namespace. Thus it is ensured that all your code may be resolved at compile-time.
Basically these are the only restrictions to your code. Though I should note that in a default configuration the namespacePortR will not resolve things like $className = 'Some\\NS\\Class'; new $className, because it would require inserting additional code. It's better that this is patched up later (either manually or using an automated patching system.)
Configuration
As we have made the assumption that no global function or constant is redeclared in a namespace you must set the assumeGlobal class constant in the namespace listener. In the same file set the SEPARATOR constant to _.
In the namespacePortR change the configuration block to satisfy your needs.
PS: The script may be provided a ?skip=int option. This tells it to skip the first int files. You should not need it, if you have set the override mode to intelligent.
Here's what I've found:
Doing this with regular expressions is a nightmare. You can get most of it done with just a few simple expressions, but then edge cases are a killer. I've ended up with horrible, fragile mess that barely works with one codebase.
It's doable with built-in tokenizer and simple recursive descent parser that handles only simplified subset of the language.
I've ended up with rather ugly design (parser and transformer in one – mostly just changing or re-emitting tokens), because it seemed too much work to build useful syntax tree with whitespace maintained (I wanted resulting code to be human-readable).
I wanted to try phc for this, but couldn't convince its configure that I have built required version of Boost library.
I haven't tried ANTLR for this yet, but it's probably the best tool for that kind of tasks.
I am working on a project that emulates PHP 5.3 on PHP 5.2: prephp. It includes namespace support (not yet complete though.)
Now, out of the experience of writing this there is one ambiguity problem in namespace resolution: Unqualified function calls and constant lookups have a fallback to the global namespace. So you could convert your code automatically only if you either fully qualified or qualified all your function calls/constant lookups or if you didn't redefine any function or constant in a namespace with the same name as a PHP built in function.
If you strictly adhered to this practice (whichever of them you choose) it would be fairly easy to convert your code. It would be a subset of the code for emulating namespaces in prephp. If you need help with the implementation, fell free to ask me, I would be interested ;)
PS: The namespace emulation code of prephp isn't complete yet and may be buggy. But it may give you some insights.
Here's the best answer I think you're going to be able to find:
Step 1: Create a directory called 5.3 for every directory w/ php5.3 code in it and stick all 5.3-specific code in it.
Step 2: Take a class you want to put in a namespace and do this in 5.3/WebPage/Consolidator.inc.php:
namespace WebPage;
require_once 'WebPageConsolidator.inc.php';
class Consolidator extends \WebpageConsolidator
{
public function __constructor()
{
echo "PHP 5.3 constructor.\n";
parent::__constructor();
}
}
Step 3: Use a strategy function to use the new PHP 5.3 code. Place in non-PHP5.3 findclass.inc.php:
// Copyright 2010-08-10 Theodore R. Smith <phpexperts.pro>
// License: BSD License
function findProperClass($className)
{
$namespaces = array('WebPage');
$namespaceChar = '';
if (PHP_VERSION_ID >= 50300)
{
// Search with Namespaces
foreach ($namespaces as $namespace)
{
$className = "$namespace\\$className";
if (class_exists($className))
{
return $className;
}
}
$namespaceChar = "\\";
}
// It wasn't found in the namespaces (or we're using 5.2), let's search global namespace:
foreach ($namespaces as $namespace)
{
$className = "$namespaceChar$namespace$className";
if (class_exists($className))
{
return $className;
}
}
throw new RuntimeException("Could not load find a suitable class named $className.");
}
Step 4: Rewrite your code to look like this:
<?php
require 'findclass.inc.php';
$includePrefix = '';
if (PHP_VERSION_ID >= 50300)
{
$includePrefix = '5.3/';
}
require_once $includePrefix . 'WebPageConsolidator.inc.php';
$className = findProperClass('Consolidator');
$consolidator = new $className;
// PHP 5.2 output: PHP 5.2 constructor.
// PHP 5.3 output: PHP 5.3 constructor. PHP 5.2 constructor.
That will work for you. It is a cludge performance-wise, but just a little, and will be done away with when you decide to stop supporting 5.3.
What I did, with a large codebase that used the underscore naming convention (among others), and require_once a whole lot in lieu of an autoloader, was to define an autoloader, and add class_alias lines in the files defining aliases to a classes old name after changing their names to be nice with namespaces.
I then started removing require_once statements where execution was not dependent on inclusion order, since the autoloader would pick stuff up, and namespace stuff as I went along fixing bugs and so on.
It's worked quite well so far.
Well, I don't know if it is the "best" way, but in theory, you could use a script to take your 5.3 migrate code and backport it into 5.2 (potentially even using PHP).
On your namespace files you would want to do something convert:
namespace \Project\Directory\Filename;
class MyClass {
public $attribute;
public function typedFunction(MyClass $child) {
if ($child instanceof MyClass) {
print 'Is MyClass';
}
}
}
To something like:
class Project_Directory_Filename_MyClass {
public $attribute;
public function typedFunction(Project_Directory_Filename_MyClass $child) {
if ($child instanceof Project_Directory_Filename_MyClass) {
print 'Is MyClass';
}
}
}
And in your namespace code you would need to convert from:
$myobject = new Project\Directory\Filename\MyClass();
To:
$myobject = new Project_Directory_Filename_MyClass();
While all your includes and requires would stay the same, I think you would almost need to keep some sort of Cache of all your classes and namespace to do the complex conversion around the 'instanceof' and typed parameters if you use them. That is the trickiest thing I can see.
I haven't tested this on my own, but you may take a look on this php 5.2 -> php 5.3 conversion script.
It's is not the same as 5.3 -> 5.2, but maybe you will find some useful stuff there.
Our DMS Software Reengineering Toolkit can likely implement your solution pretty well. It is designed to carry out reliable source code transformations, by using AST to AST transforms coded in surface-syntax terms.
It has a PHP Front End which is a full, precise PHP parser, AST builder, and AST to PHP-code regenerator. DMS provides for AST prettyprinting, or fidelity printing ("preserve column numbers where possible").
This combination has been used to implement a variety of trustworthy PHP source code manipulation tools for PHP 4 and 5.
EDIT (in response to a somewhat disbelieving comment):
For the OP's solution, the following DMS transformation rule should do most of the work:
rule replace_underscored_identifier_with_namespace_path(namespace_path:N)
:namespace_path->namespace_path
"\N" -> "\complex_namespace_path\(\N\)"
if N=="NCLASS_OR_NAMESPACE_IDENTIFIER" && has_underscores(N);
This rule finds all "simple" identifiers that are used where namespace paths are allowed,
and replaces those simple identifiers with the corresponding namespace path constructed
by tearing the string for the identifier apart into consitutent elements separated by underscores. One has to code some procedural help
in DMS's implementation langauge, PARLANSE, to check that the identifier contains underscores ("has_underscores"), and to implement the tear apart logic by building the corresponding namespace path subtree ("complex_namespace_path").
The rule works by abstractly identifying trees that correspond to language nonterminals (in this case, "namespace_path", and replacing simple ones by more complex trees that represent the full name space path. The rule is written as text, but the rule itself is parsed by DMS to construct the trees it needs to match PHP trees.
DMS rule application logic can trivially apply this rule everywhere throughout the AST produced by the PHP parser.
This answer may seem overly simple in the face of all the complicated stuff that makes up the PHP langauge, but all that other complexity is hidden in the PHP langauge definition used by DMS; that definition is some 10,000 lines of lexical and grammar definitions, but is already tested and working. All the DMS machinery, and these 10K lines, are indications of why simple regexes can't do the job reliably. (It is surprising how much machinery it takes to get this right; I've been working on DMS since 1995).
If you want to see all the machinery that makes up how DMS defines/manipulates a language, you can see a nice simple example.