Modularity in PHP

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Gambler
Forum Contributor
Posts: 246
Joined: Thu Dec 08, 2005 7:10 pm

Modularity in PHP

Post by Gambler »

How do you achieve modularity of your applications? Do you use some kind of wrapper for include_once or class autoloader? What about directory structure and class names? Table names, DB use?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

__autoload() :)

As for path names I'd go with whatever makes sense to you.... I usually just have /app/classes/classname.class.php /app/interfaces/classname.interface.php and /app/templates/name.tpl.php :) That's purely a personal preferecne though... i haven't researched it in any way, it just seems logical.
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Post by josh »

All path names are set with define(), that would be a start

Are you talking about allowing plugins? Just have a plugin loader class that expects your plugins to have a "main" class that adhere's to a specific api, let the plugin's author worry about the internals of their plugins as long as you can interface it ok. You would obviously need to expand upon this when you want to allow the plugin to start interfacing your application directly instead of the other way around but you can get pretty creative with that one way communication

You can also set up a class that the plugins can interface to talk to other plugins, etc... It will just take a lot of storyboarding / planning to get it just right the first time around.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Modularity in PHP

Post by Christopher »

A lot of big questions there. Probably a long thread to cover each. It reminds me of the old saying: How do I get to Carnegie Hall? Practice, practice, practice.
Gambler wrote:How do you achieve modularity of your applications?
There are lots of answers to this. My general opinion (others will disagree) is that the state of the art is to use many small classes to build up you application. This supports DRY and is the key to making things testable which is central to Agile methodologies.
Gambler wrote:Do you use some kind of wrapper for include_once or class autoloader?
You can use the __autoload function, but you might just want to include/create your object directly until you actually have a complexity problem. There are many ways to solve this and get Lazy Load that __autoload gives you. Probably the most common is to dispatch everything that PHP programmers would normally consider a "page" (usually refered to as an action) as an object using the Command pattern. This is how most controller based architectures work. The Front Controller is probably the most commonly used approach for this. All this usually heads toward a PHP spin on MVC which is a very long discussion in itself.
Gambler wrote:What about directory structure and class names?
I try to keep all code and templates out of HTML space with only the controller scripts, image, javascript and CSS in the public directory. Naming is personal and pretty trivial it ends up. There is much more agreement than all the flamewars would indicate. The PHP site has some naming conventions as I recall -- they are probably fine.
Gambler wrote:Table names, DB use?
Tables names like all naming is up to you and not worth the litany of opinions. For DB use (and I assume you mean implementation, not server type) the most important thing is that there is separation between you data layer and your presentation layer. If you achieve that you are 90% of the way to a solid architecture. Beyond that the question becomes what kind of functionality you need. A basic Connection object is the norm. It can return a dumb Record Set or smarter Active Record. Or better would be to create Gateway classes for your data access. Table Data Gateway is simple and straightforward. If you don't control the database and it changes regularly then you might want to implement a Data/OR Mapper to translate between your objects and the database schema to insulate yourself from those changes.
(#10850)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I usually run some kind of database install script, where a where an admin can dynamically control what plugins are to be loaded.
It certainly isn't required to get an efficient plug in system, but it really comes down to personal preference (as long as its not stupidly complex)
Gambler
Forum Contributor
Posts: 246
Joined: Thu Dec 08, 2005 7:10 pm

Post by Gambler »

__autoload()
Autoload itself isn't modular. You can declear it only once.
Are you talking about allowing plugins?
Yes and no. Word "plugins" assumes that there is a core system, which barkes the rules. Also, it assumes that other classes are written specificly for that core system.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Gambler wrote:
__autoload()
Autoload itself isn't modular. You can declear it only once.
Are you talking about allowing plugins?
Yes and no. Word "plugins" assumes that there is a core system, which barkes the rules. Also, it assumes that other classes are written specificly for that core system.

Code: Select all

<?php

if ((function_exists('__autoload')) === FALSE) {
    function __autoload($class)
    {
        include_once($class . '.php');
    }
}

?>
User avatar
CoderGoblin
DevNet Resident
Posts: 1425
Joined: Tue Mar 16, 2004 10:03 am
Location: Aachen, Germany

Post by CoderGoblin »

Table names, DB...

Rather than go into PHP I would like to point out that database table names should all be lower case with the name as plural, as descriptive as possible. For instance rather than "UserName" use "user_names". Don't be tempted by abbreviations as you are unlikely to remember them a year down the line. 'uname' is one I have seen before. There are some useful guidelines as far as databases naming conventions/good practice but I cannot think of any links off-hand and I have never stored them.

Key thing with anything like this is build everything on the assumption that someone else may need to use your code/structures. Keep it as descriptive as possible while still being simple.
Gambler
Forum Contributor
Posts: 246
Joined: Thu Dec 08, 2005 7:10 pm

Post by Gambler »

Jenk, this doesn't achieve anything, because other __autoload might use completely different coventions. So it would be defined, but your script would break anyway.

Currently, I'm using load().

Code: Select all

<?php
function load(){
    $args = func_get_args();
    $result = TRUE;
    foreach ($args as $name) {
        $file = 'tools/'.$name.'.php';
        $result = $result && (bool)include_once($file);
    }
    return $result;
}
?>
Not the best way either.
User avatar
cj5
Forum Commoner
Posts: 60
Joined: Tue Jan 17, 2006 3:38 pm
Location: Long Island, NY, USA

The C.J. Code Modularity Project

Post by cj5 »

Well, I start with 1) a DB table, 2) two PHP classes (one for frontend, one for admin), and a template.

1) The tables always have several columns in common:
a) 'id' The primary key (always available, never excluded)
b) Depending on the situation, the table will include a name/title field, which is given 'title'
this is usually for the purpose of naming whatever it is we are storing.
c) If necessary (when making one-to-one relationship), I include a ID column for an external table relation connection.
d) In considering relational data, I will sometimes include two more tables for one-to-many relationships (so that makes 3 tables; one
this data, one for the relational data, and the connector)
e) As a standard, always include an 'insert_stamp' and 'update_stamp' column, for time logging

2) PHP classes for data manipulation and output:
a) Frontend class:
i) Include all SQL methods for fetching any data I need
ii) Data formatting methods (takes data arrays, and gets them ready for the template)
iii) Establish all page, DB, server, and template information through class variables (this will allow you to plug these classes into your site anywhere, without having to change links, table, or path information)
b) Backend/Admin class (extends the frontend class):
i) Data manipulation methods (insert, updates, etc... for add, edit, delete...)
ii) File uploading processing and methods
iii) Form validation and processing

3) Template: (pretty self-explanatory)

Most of this comes from my information management background, and the fact that I am very OC about my code, when it comes to getting it organized, and keeping it that way. I also do consulting for a fee. =)
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Gambler wrote:Jenk, this doesn't achieve anything, because other __autoload might use completely different coventions. So it would be defined, but your script would break anyway.

Currently, I'm using load().

Code: Select all

<?php
function load(){
    $args = func_get_args();
    $result = TRUE;
    foreach ($args as $name) {
        $file = 'tools/'.$name.'.php';
        $result = $result && (bool)include_once($file);
    }
    return $result;
}
?>
Not the best way either.
You misunderstand the purpose of __autoload..

Code: Select all

<?php

$class = new Class;

?>
Would fail, no class definition found..

Because many developers separate their classes into files of their own, usually sharing the same name as the class definition within, the php bod's decided to try and take a leaf out of Java (and other's) book and allow for the use of auto-inclusion. This could not be fully implemented as it is a late addition, thus so we now have the ability to switch this feature "on" by defining the __autoload function.

Code: Select all

<?php

if ((function_exists('__autoload')) === FALSE) {
    function __autoload ($classname)
    {
        include_once($_SERVER['DOCUMENT_ROOT'] . 'includes/' . $classname . '.inc.php');
    }
}

$class = new Class;

?>
Would not fail, providing you have the class deifnition for Class in a file called Class.in.php in the /doc-root/includes/ directory.

All the if is for is so you can nest the __autoload function within the various files that you might need to include in your project and not get function already defined errors.
Gambler
Forum Contributor
Posts: 246
Joined: Thu Dec 08, 2005 7:10 pm

Post by Gambler »

Okay, let me give an example.

You have:

Code: Select all

<?php
if ((function_exists('__autoload')) === FALSE) {
    function __autoload ($classname)
    {
        include_once($_SERVER['DOCUMENT_ROOT'] . 'includes/' . $classname . '.inc.php');
    }
}
?>
I have:

Code: Select all

<?php
if ((function_exists('__autoload')) === FALSE) {
    function __autoload ($classname)
    {
        include_once('tools/'.$classname.'.php');
    }
}
?>
Now I want to use your classes (modules), which rely on __autolod(). Ta-dam! Conflict. Thus, code with __autoload() is not modular.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

That is a problem not just limited to __autoload() and is infact a problem that will never be solved.

My classes use the pseudo-global $MY_SUPER_GLOBAL; to gain and pass through various details that are available to the entire application..

Yours uses $MY_GLOBAL_CONFIG; .. Ta-da! Conflict.

Atleast with __autoload() you can separate this even further in two ways..

__autoload.php (or prepend to a generic 'config' file that many applications use):

Code: Select all

<?php

if ((function_exists('__autoload')) === FALSE) {
    function __autoload($class) {
        include_once("includes/{$class}.inc.php");
    }
}

?>
Then in any other page:

Code: Select all

<?php

include '__autoload.php'; //or swap for your generic config file which could have this in.

?>

OR, set your include_path to also search your includes directory, and not use any path in the autoload function, only the filename. File extension will just need to be arbitrary.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Gambler wrote:Okay, let me give an example.

Now I want to use your classes (modules), which rely on __autolod(). Ta-dam! Conflict. Thus, code with __autoload() is not modular.
As I said, you might want to start out just including files directly with the path specified. If you don't have a complexity problem managing the paths then that is the most straightforwared. A reasonable alternative to __autoload() is something like this:

Code: Select all

$path .= PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] . 'includes/'  ;
$path .= PATH_SEPARATOR . 'tools/'  ;
set_include_path(get_include_path() . $path);
// and then
include_once('Classname.php');
$obj = new Classname()
You can also you PEAR style naming and covert underscores to forward slashes in your __autoload() but you will need to be careful of capitalization.
(#10850)
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

CoderGoblin wrote: I would like to point out that database table names should all be lower case with the name as plural
That's clearly a matter of preference. For example, IDEF1X suggests that you name your entities (tables) using singular nouns written in upper case. Indeed, "SELECT `user`.`name` FROM ... " looks more natural to me than "SELECT `users`.`name` FROM ..." (I don't like to use upper case for table names).
Post Reply