PHP and server hierarchy design, inspired by change of url propagation - php

I had a general question on proper design of a php files and their storage on a server.
The problem is this: I had split a php object's functions into different php files, something like:
File 1 AndroidFlashCard.php
class AndroidFlashCard {
public function retrieveCards($packname){}
public function retrievePacks(){}
....
File 2 RetrieveCards.php
include ($_SERVER['DOCUMENT_ROOT'].'/flash_card/AndroidFlashCard.php');
$connection = new AndroidFlashCard();
$connection->retrieveCards($_REQUEST['pack']);
...
Besides the bad code smell regarded making seperate php files for a single function calls, the problem happens when the location/name of class AndroidFlashCard changes. Suppose we go with this shoddy design and I have 1000 different functions...
The quick and dirty solution that came to mind was to have ANOTHER include file:
File 3 include.php:
include ($_SERVER['DOCUMENT_ROOT']./[location of class])
But this doesn't really change anything, because if the location of the include.php file changes, I'd have to make 1000 changes again.
So let's think about this. If I have to keep making 1000 changes to 1000 php files that only include a reference to a class and then code to execute a function, then maybe THAT is the design problem.
In android, I only know how to execute http requests, which is why I split the function calls into separate files.
If I could get hold of that php object, it would make things easier, but I've a feeling this will be difficult to accomplish.
What is the simpler solution? Is writing directory structures a design part of production work, which after approval, is simply just written in stone?

This sounds like a good case for class autoloading. When you attempt to use the class in say file 2, then php can call an autoload function to actually find the class to include. You will have to write the logic behind it, but thats 1 change rather then a 1000 as you say.
__autoload($class_name)

It's generally a good idea to have all class functions inside the class, only only separate classes into different files. As for directory and code structure, you could have a look at how frameworks do it. Like the Model-View-Controller pattern, has an already thought out code structure. Spesifically, you could have a look at the Zend Framework, and simply use the predefined directory structure which the Zend MVC ships with. Using the Zend autoloader, you don't need to include_file() at all, if you stick to the Zend namespace naming conventions.

Related

PHP - Using spl_autoload_register with subfolders

So I am using spl_autoload_register to load my classes. However I have this structure for my classes:
classes
classfolder
classfile
So normally when I was including them I was doing this:
include('classes/modules/module_class.php');
Now using spl_autoload_register how would I handle sub folders? I dont have the ability to upgrade to php 5.3 to use namespaces, do I need to do something like this?
$module = new modules_Module();
Then the function I register with spl_autoload_register explodes the _ and uses the first part as the class folder and the second part as the class method. But then I would have to change the class name to modules_Module right? If so is that ok? Or is there a better way of doing this?
Not saying you should do that, but a common pattern in PHP-land is to use case in the filenames as well and not add stuff like _class or class.Module.php:
include('classes/Modules/Module.php');
In PHP 5.2 (no namespace support), you then work with the _ underscores:
class Modules_Module
{
}
So this is how it is normally done. Not saying that you must do it that way. You can also just resolve as you wish. But one should also know about this more common pattern as it helps reading/navigating third party sourcecode.
Basically that's it.
In addition to namespaces (which you can't use) and naming conventions on class names (which would need you to rename all your classes) the only other sane option is to build a classmap: an array that maps class names to the full path of the file that defines them.
Going this way means that you have to update the classmap each time classes are added or modified, a task which is a prime candidate for automation. Look into using Composer's autoload functionality, it can do this on demand and generate the autoload code for you automatically. Composer is so good that it's useful even when you disregard 90% of its features.
As long as your files are arranged in some way that let's you go from the class name to the file name, you can implement your autoloader function however you like. Your autoload function needn't just turn the class name into a directory path: it's job is simply to take a class name, and include the right file, if at all possible.
Keeping your mapping relatively simple is useful for humans, too, though - so you know which file to edit.
For instance, you could say that all classes beginning Mod_ live in a directory called classes/modules but all others live in a directory called classes/general.
You can also use file_exists to check multiple possibilities for one class, but bear in mind that this has some cost, both to PHP checking the filesystem, and to the human trying to remember where the file went.

Autoloading from file with several classes

In my project structure I have the usual class per file separation and auto-loading is easy, but now I would like to group some small classes in a a single file, probably a file with the name of the current folder.
now:
|/module
|-Smallclass1.php
|-Smallclass2.php
|-Smallclass3.php
|-Normalclass.php
after:
|/module
|-module.php <-- smallclasses go here
|-Normalclass.php
Now comes the debate.
To autoload one 'SmallclassX' I was thinking to check if SmallclassX.php file exists if not check if module.php exists and if so, check if the class exists inside the file and include it. Including the whole module.php file when I need a single class seems an overhead specially if the file contains many classes. Here they suggest using tokens the check if the class exists, but I'm not sure about it's efficiency, after that I'll need a method to include only the class I need.
Do you think if I get to load only the class I need like I mentioned, will it be also an overhead because of reading the file more that once and looking inside to include the piece of code I want?
You can stack autoloaders using spl_autoload_register, allowing you to first attempt to load the class from a dedicated file, then falling back to including the module file afterwards (and if no autoloader can solve the dependency, error out normally). This will allow you to avoid all hacks by parsing tokens and other items, which will require a lot more than just require-ing the file and seeing what the result is afterwards.
I would however advice you to benchmark this solution. Whether it's more effective will depend on the delay for accessing the files (APC) and the cost of parsing and including each class seperately.
I'll also agree with the other comments, it might be confusing that you have two separate schemes for including classes, and APC will remove most of the cost of having separate files anyway.

Using spl_autoload without use keyword on every page?

We have recently been rewriting our PHP application's model code using OO. We have the classes done, and we included namespacing (in the form of Project\Level1\Level2 or whatever). Since this is an already-built application, there are a lot of classes. We are using an autoloader, for ease of loading the classes, which is great. But here's where the hassle comes in.
We have probably 300 pages strewn all across the application, and all of them require a PHP file at the very beginning (a sort of bootstrap script). The autoloader script is required in that bootstrap file.
Now, most of our pages will be using at least one but likely many of the classes. We know that we have two options here:
//either call the classes using qualified names
$person = new Project\Person;
//or include the "use" keyword on every page, so we can alias the classes for ease of use
use Project\Person as Person;
$person = new Person;
But the hassle is that we would really rather not have to do this on every single page in our application:
require_once('../php/bootstrap.php');
use Project\Person\ as Person,
Project\Address\ as Address,
Project\Group\ as Group
Project\CustomField\ as CustomField;
$person = new Person;
$address = new Address;
We tried, for the heck of it, including the use keyword for all our classes in the bootstrap file, but according to the PHP manual, "Importing rules are per file basis, meaning included files will NOT inherit the parent file's importing rules."
It just seems like including these sprawling use statements on every page of our application is creating more hassle than it's worth. And what if we are using 15 classes on one page? Then the use statement would be huge.
My question is this: is it too much hassle? Are we correct in using namespaces, or is it not all that helpful in PHP? And now knowing what we are trying to do, are we doing it right? Is there something we are missing? Should we be calling classes with fully qualified names all the time or something?
(this is my first question on stackoverflow, so please let me know if I need to do anything differently or better with my questions in the future)
While using vanilla Zend Framework Autoloader might not be they way you want to go, the Zend Loader does support custom autoloaders, that can respect namespaces specified in-line or dynamically at run-time. In your case, you are using use Project\Person\ as Person which can become cumbersome, as you already noticed.
Using namespaces with the autoloader will allow you to take advantage of auto loading using simple path traversal to organize classes, without having to append large paths just to get to the interesting parts.
So, instead of new Corp_Assets_Generic_Person_Address, you could keep the folder structure and use new Namespace_Person_Address.
Namespaces avoid clashes in class names. Specially in large applications implementing autoloaders that need to load the right class from a number of different libraries. Using shorter class names makes the code easier to read but may make it harder to maintain. Adding the use statements makes it easier to read and later maintain but then it would be cumbersome to write and refactor. Documenting the expected behavior of the autoloader and using custom autoloaders to make more readable class names would be my suggestion.
In php.ini add your file as a prepend_file

__autoload vs include family

I've discovered the __autoload function today and after reading the official manual page of this function there's a point I don't get at all.
What's clearly the difference between using __autoload() and let's say require_once?
because it looks autoload is the new fashion way of doing the required includes but to me it's far better to use require_once instead. Hence, __autoload has to be defined into all the php files which means writing its code down there, if I put ALL my includes/require_once/...etc into one file let's call it main_header.php then all I'll need to do in my web app files is write one line of code:
<?php require_once('main_header.php'); ?>
Am I wrong somewhere?
I can see two things going for autoloading (not necessarily __autoload; prefer the more modern spl_autoload_register instead):
You don't need to explicitly include the classes. Of course, you could make a main_header.php as in your example, but then the next item comes into effect.
You don't have to load 100 classes if you are only going to use 10 of them.
It's also worth to point out that autoloading is also triggered when unserializing an object of a class that has not been yet defined, which makes things infinitely more practical. Of course there is another hook in unserialize for this (the configuration setting unserialize_callback_func), so autoloading is not technically necessary. It's definitely nicer though.
First: Use spl_autoload_register() instead of __autoload().
The autoloader is called, when you try to access a class, that doesn't exists. With include() you just include a class. The main difference is, that using an autoloader the class is only included, if its really required/used.
Usually you define an autoloader in one file and include this one on every request. If you use bootstrapping (meaning: a single file, that catches every request and redirects it to the appropriate target) its only required to define the autoloader there. So its its not required to define it in every file.
Autoloader is used for lazy initialization. It's the most effective with MVC architecture, not with websites that include a file here and there and define db connection string in every file (which is terrible).
Using autoload with MVC framework saves resources and brings a lot to modularity. Since you don't have to include files with classes, you just instantiate a class you require in the controller you're currently at.
Basically, it's an OOP thing. You shouldn't worry about it if you're not using object approach to structuring your website and include/require is what will work for you.
I think it comes down to personal preference. For me, autoload is a typical php kind of thing - a dirty "magic-quotes"-alike hack, that only exists because they don't bother to sit down and code a clean solution to the problem - in this case, an adequate packaging system + a catchable ClassNotFound exception. Until we've got this fixed, I'll stick to include.
Choose the right tool for the job. Autoloading is for loading the source-code of yet undefined Classes prior use. So you can instantiate classes you have not required the files for so far.
This makes sense if you work with classes and you want to require files at a central place, a single point instead all over in different files.
But if you're using includes and requires to let's say build a website with menu and footer, this does not make sense. Autoload does not work for this:
<html>
<head><title>Reciepes collection - My Homepage</title></head>
<body>
<?php include('header.html'); ?>
<h1>My Reciepes collection</h1>
<p>Cooking is one of my favorite hobbies .... (</p>....)
<?php include('footer.html'); ?>
</body>
</html>
If you're making use of requires to load functions, you can not use autoload either.
So only because autoload looks somewhat fancied by others to you, it does not mean that it fits your needs.
I personally use it in projects where I do not want to take care of the requires for classes and where I want to be able to dynamically load classes from within a modular directory and library structure.
I only make use of spl_autoload_register() to register one or more autoload implementations. It's the recommended method to register an autoload function in PHP as it allows to have multiple autoloaders.
All the answers are more than correct and adequate. I just thought I should add my two cents.
First, *_once functions are slow. Do not use them.
Secondly, in an app I am developing, I have lots of classes which I rely on each other and I do not always know which classes are needed in a page.
Instead of creating a main.php page and including all the classes which are a lot; obviously wasting resources and time (Include and require are expensive), I use autoload to include those files as and when they are needed by the page.
All I do therefore is code and the classes are added automatically.
This also improves your code and directory structure since you follow a system in naming your classes and files.
Just an addition. In terms of resource usage and speed(decreasing order of requirements and increasing order of speed):
Require_once() ; include_once() ; require() ; include()
Making include the best and fastest.
Autoload and the include family functions both have their place.
Autoload is intended for lazy loading of modules in an OO based script (namely classes and interfaces) and is invoked when you use new or statically call a method of a class that hasn't yet been loaded. This triggers autoload to run a function that attempts to load (typically via include) the class or interface in question. The advantage of this approach is that classes are only loaded as and when they are needed. Any class that doesn't get used doesn't get loaded.
Autoload cannot be used for loading program fragments that aren't a class or an interface, for those items you still need the include family.
As for having to add an autoload function to every script that needs to autoload? In this case you put the autoload function in its own file and include it in the scripts that need to autoload. :)
Another note: include and require may become a big pain while unit testing.
I don't know how should I prevent requiring / including anything. There is test helpers, that can do some useful things, such as preventing exiting or dieing, but they still cannot mock include or require.
Mockery library recommends using autoloading to be able to mock public static methods.

How to leverage PHP's autoloader functionality in such a way, that it searches through a defined set of paths?

One bad thing about using the autoloader is, that it doesn't know where to look at. My framework already has about 70 classes in about 15 different directories. Because of that, I didn't go for autoload, but instead struggle around with generating paths. So I have an associative paths array that give me paths from my root file to where I want to go. It works, but it's really becoming a pain.
First, the user of my framework will very likely have no big idea where a class resides. So creating a FormViewController is already a pain just because you need the path to include it.
For the beginning, I think it would be just fine if PHP had to "search" for the file through multiple directories when it wants to autoload it. So what? Shall PHP do so and save us a lot of pain. But how could I do that most efficently?
Ther's an array called $paths which I get out of a global context object. The paths could be defined in the array in the most reasonable order: Starting with the most frequently used paths.
I know the PEAR library has a funny naming convention like system_views_specialviews_SpecialView.php which gets converted in the Autoloader to system/views/specialviews/SpecialView.php ...but honestly, that's horrible. Don't want to mess up my file names like this. For the developer who creates his own classes that would suck just straight away.
Before I re-invent the wheel for this task, maybe someone knows a solution for this?
NB: I'm presuming you're creating your own custom autoloader via spl_autoload_register, etc.
If you're creating an autoloader and the classes you're looking to include are spread across 15 different directories, I don't see how you can hope to avoid using some form of class name to directory path mapping, although this obviously doesn't need to be quite as deep as the example you quote.
At a simple level, if you keep all of your system libraries in a single area, then a quick case match as such...
switch(true) {
case substr($className, 0, 6) == 'System': { $libraryPath = 'xxx'; break; }
...
}
...should be reasonably efficient approach, although it obviously depends on how many paths you need to manually check.
Additionally, I presume you're checking for the existance of the class prior to attempting to autoload? (i.e.: You're doing a class_exists($className) to ensure it's not already been included.) Then again, the requirement for this depends on how often you re-use objects.
You might want to look at Nette Framework's RobotLoader.
Basic idea is that it scans through directories you specify, parses .php (token_get_all) files and finds out in which file are which classes/interfaces. Locations of classes/interfaces are cached, so it is fast. Loader is registered with spl_autoload_register() and when PHP interpreter cannot find some class, it calls loader's tryLoad() method.
I'm sure you already know this, but just in case, you can write an __autoload() (API) function that takes the class name as a parameter in your front controller (or in any file included by it) that searches your directories based on any criteria that you want. All you do is require_once the correct file once you've found it, or throw an exception if the class name isn't valid.
From your description, I didn't get any indication that you were doing this. It sounds like you are just appending PHP's include path with values in unwieldy global arrays.
The PHP Autoloader does all you want. If you have different locations you just have to create several instances of the autoloader for each class path.
It seems it does the same like the mentioned RobotLoader, except the first searching is done with some intelligence. Files with lower Levenshtein distance to the class name are prefered as potential class definitions. But this is only relevant for the first search without the index.

Categories