Jump to content

Recommended Posts

Hi everyone!

 

This is not exactly related to a scripting issue. Basicly what I want to do, is make a library of functions and classes that I can use later on to ease my work. But I'm trying to find a good way to do this, but still no luck. The functions are all planned out. It's the structure of the files and folders that's beeing a pain in the ass.

 

Also I'm not sure if I should use only classes, only functions or a mix of both. I've tried doing a mix of both, but then, often I found my self unable to decide if the newly created function should be a part of a class or not...

 

The way i've tried setting it up now is this

root

|-library

|--class

|---file.class.php

|--libs

|---file.lib.php

|-init.php (Includes some path definition and functions to load everything needed)

index.php

 

All ideas are more than welcome

Thanks in advance

 

Greets,

Ayon

Link to comment
https://forums.phpfreaks.com/topic/188398-i-could-use-some-tips-and-ideas/
Share on other sites

It would help if we knew what kind of project you were doing, looking from the structure, I've not used something like that since I've worked with C++. It's essentially pointless to split all your 'library' classes into different files under another root. If you were to create a huge program with wrappers to the database and required more than you'd expect, then maybe you should use an infrastructure like so.

 

I found my self unable to decide if the newly created function should be a part of a class or not...

 

What do you mean? Every single function should not have to be within a class, if the class is relevant (such as Employees extends Workers), then the returnTax() function should probably be included, but not the addTwoPlusTwo().

It would help if we knew what kind of project you were doing

 

Nothing special in particular at the moment.. Except this.. This is just going to be something that contains useful custom php functions that I can just keep on building on as I learn new stuff or work on projects..

 

Sorry no.. Not really

- - - Functions

- - - Validation

- - - Other crap?

if that ment to be files or folders?

What TheFilmGod stated is what most developers use (in case of exportation or clean coding) if they are going to separate their libraries as so.

 

/LIB

      /CSS

              /etc.css

      /PHP

              /Functions.php

              /Validation.php

              /Database.php

      /JS

              /AJAX.js

              /jQuery.js

etc.

 

useful custom php functions that I can just keep on building on as I learn new stuff or work on projects..

 

That structure is pretty much as good as you're going to get, if you want to re-use libraries and classes for your new work, as you can call the libraries in a fashion you intended.

 

But again: you will be using old code.

You have two concepts to organize here:

1) Your framework / library

2) Your application(s)

 

Your framework should be mostly objects and I don't mean just functions wrapped up in the namespace of an object.

 

I would organize the framework like so:

# Let / denote the home directory of the framework, such as:
#       /home/myframework
#       c:\myframework
/bootstrap.php                  # By including this file, your application will 
                                # gain all functionality of the framework.
/modules                        # Directory - modules or plugins go here.
/modules/auth                   # Directory - Contains source code for your
                                #   authentication module.
/modules/auth/_base.php         # Defines abstract class Authentication_Base
/modules/auth/active_directory.php  # Defines class Active_Directory_Authentication
/modules/auth/database.php      # Defines class Database_Authentication
/classes                        # Directory - core framework classes (those not
                                #   part of a module) go here.
/classes/Application.php        # File - Defines class Application
/classes/Controller.php         # File - Defines class Controller
/classes/Layout.php             # File - Defines class Layout (or Template)
/classes/View.php               # File - Defines class View
/classes/Request.php            # File - Defines class Request
/third_party                    # Directory - Third party code (such as
                                #   PHPMailer) goes here.
/third_party/PHPMailer          # Directory - PHPMailer source code
/third_party/Spreadsheet        # Directory - Spreadsheet related source code
/utility                        # Directory - Any code that is procedural (think
                                #   functions or classes with all static
                                #   methods) goes here.
/utility/string.php             # File - Defines StringUtil class
/utility/array.php              # File - Defines ArrayUtil class
/application_skeleton           # Directory - Contains skeleton application.
                                #   Directory structure mimics that shown below.

 

I like to organize my applications like so:

# Let / denote the home directory of the application, such as:
#       /home/mycoolsite
#       c:\mycoolsite
/index.php                      # File - All requests filtered here via 
                                # .htaccess
/.htaccess                      # Handles request filtering through index.php
/private                        # All non-servable PHP files go here.  It's
                                # better to store them outside of the www
                                # directory, but that's not always possible.
/private/.htaccess              # Forbid all requests inside this directory.
/private/classes                # Application classes
/private/classes/MyApplication.php      # File: class MyApplication extends Application { }
/private/classes/MyAuth.php             # File: class MyAuth extends Database_Authentication { }
/private/classes/MyController.php       # File: class MyController extends Controller { }
/private/controllers/IndexController.php        # File: class IndexController extends MyController { }
/private/controllers/UserController.php         # File: class UserController extends MyController { }
/private/layouts/xhtml.php      # File: Defines standard template for xhtml requests
/private/layouts/xhr.php        # File: Defines standard template for ajax / xhr requests
/private/views/User             # Directory: Views pertaining to User controller
/private/views/User/list.php

 

For development and testing, your application code should be separate from the framework code.  When it comes time to deploy your code, you should be created a release with version control software and the framework should be deployed inside the application.

 

In order to accomplish this, your application index.php might look like:

 

<?php

define( 'APP_DIR_HOME', dirname( __FILE__ ) );
define( 'APP_IS_DEV', strtolower( php_uname( 'n' ) ) === 'mycomputername' );

// Require the framework
if( APP_IS_DEV ) require_once( 'c:\\myframework\\bootstrap.php' );
else require_once( APP_DIR_HOME . '/private/myframework/bootstrap.php' );

require_once( APP_DIR_HOME . '/private/classes/MyApplication.php' );
try {
    $app = MyApplication::getInstance();
    $app->Run();
} catch( Exception $ex ) {
    MyApplication::FatalExceptionHandler( $ex );
}
?>

 

Since your framework is inside version control and it contains an application_skeleton directory, starting a new project is as simple as:

 

1) Export application_skeleton

2) Rename according to project

3) Check back into version control as a new project

 

As you work on your project you will undoubtedly update framework and application code.

 

When you're ready to make a release:

1) Commit your framework

2) Tag your framework as the next release, such as 1.0.4

3) Commit your application

4) Tag your application as the next release, such as 1.0.2

 

Now if you're using SVN your repository might look like this:

/myframework
    /tags
        /myframework-v1.0.0
        /myframework-v1.0.1
        /myframework-v1.0.2
        /myframework-v1.0.3
        /myframework-v1.0.4
    /trunk
    /branches
/mycoolapp
    /tags
        /mycoolapp-v1.0.0
        /mycoolapp-v1.0.1
        /mycoolapp-v1.0.2

 

Since mycoolapp-v1.0.2 is dependent on myframework-v1.0.4, we should copy that version of the framework into the project.

/myframework
    /tags
        /myframework-v1.0.0
        /myframework-v1.0.1
        /myframework-v1.0.2
        /myframework-v1.0.3
        /myframework-v1.0.4 [copy this]
    /trunk
    /branches
/mycoolapp
    /tags
        /mycoolapp-v1.0.0
        /mycoolapp-v1.0.1
        /mycoolapp-v1.0.2
            /private
                /myframework-v1.0.4 [paste it here, because that's where index.php looks for it when deployed]

 

Then we must rename it in the application:

/mycoolapp
    /tags
        /mycoolapp-v1.0.0
        /mycoolapp-v1.0.1
        /mycoolapp-v1.0.2
            /private
                /myframework [rename it to whatever index.php expects]

 

Now to deploy your app you just export mycoolapp-v1.0.2 and it will include the correct version of the framework that you built and tested it with.

 

That's all I have time for right now; I hope it helped some.

@oni-kun: thanks for clearing that up for me :)

 

@roopurt18: well i'm not trying to build a framework. this is more like a simple library of reusable stuff that i create along the way while doing different projects or just stumble over something useful somewhere on the web. so there will be no controllers, views, models and so on.. Only like a database class containing open/close function inside it along with a function for making the sql query more sequre and performing queries... the a string class with stuff for helping out with strings... basicly for making it all abit more efficient when i'm working on projects..

simple library of reusable stuff

There is no such thing.  Oh sure, it may start simple but it'll bloat itself to no end as time progresses.  Then you'll have a library of code that is damn near impossible to refactor due to the number of your projects that rely on it.  :)

 

To each their own I suppose!

There is no such thing.  Oh sure, it may start simple but it'll bloat itself to no end as time progresses.  Then you'll have a library of code that is damn near impossible to refactor due to the number of your projects that rely on it.  :)

 

so what you're saying, is that a library of generally reusable functions is waste of time? if i could have project related and general reusable functions in separate locations?

 

right now i'm getting a feeling of that you're saying "framework or nothing" or something :D and at this point i do not have enough knowlege to make a framework... :P

 

sorry for maybe asking stupid questions... just trying to understand

 

Thanks for all replies so far

Oh that's not what I'm saying at all.  A library of re-usable functions is certainly better than nothing and it's a great stepping stone in your understanding of creating something like a framework.

 

Libraries of functions do tend to grow ungainly over time though.  The thought process will be something like "Does this function go in my library or the application?"  Also, "hooking" your application to the library can be troublesome on occasion.  However with abstract classes and interfaces you can define a base functionality and then the derived, application specific code can implement the pieces that are necessary.  The best part is the code won't work until you do.

 

On a brighter note, a framework doesn't need to be some daunting task.  When I started at my current job I spent 3 or 4 days building the core of the framework that I still use today in all of my applications.  I've worked there for two years now, give or take, and the entire framework is maybe 15 files.  All it needs to do is expedite the most common and core needs of your application.  It doesn't need to be nearly as complicated as the common PHP frameworks that exist today.  (As a side note, I think almost every existing PHP framework is inconsistent in it's implementation and poorly documented in terms of best practices.  I'm not encouraging you to make another one of those!)

 

Another nice thing about a framework is the framework will dictate how an "application" works.  It will do this with the base classes and then the derived application-specific classes will override methods where necessary.  The nice thing about this is once your framework becomes mostly static and stable, as mine has at work, it will not change much.  Each and every one of your applications you deploy, however, will all work the same.  When you look at a project 6 months after it's been deployed, you won't have to ask yourself "How did I implement the XYZ in this one again?"  You'll know how the framework implements XYZ because it's the same for all of them.

 

A library of function is just that, a collection of functions.  They don't define behavior, which when you support many applications and they're not always fresh in your mind is very important when it comes to support.  The more consistent the behavior of your applications, the less time you spend looking_at_code_with_really_long_function_names().

 

Anyways, that's just my opinion and food for thought.  We all start somewhere.  :)

thanks alot for a very well explained reply roopurt18... the reason why i've been so "scared" of doing a framework is prob cause i've only looked at the sources of zend, cake and codeigniter.. think i should start studying smaller frameworks to get a better understanding without getting so confused...

 

again thanks alot for the help

Here's a list of frequently used directory layouts: http://framework.zend.com/wiki/display/ZFDEV/Choosing+Your+Application%27s+Directory+Layout

 

Generally I adhere to two different directory layouts

 

dedicated/vps

private_html
`- libraries
`- modules
    `- install
`- templates
    `- helpers
    `- scripts
`- settings
    `- application.xml
`- workspaces
    `- system
        `- sessions
    `- user
        `- uploads
public_html
`- images
`- scripts
`- styles
`- index.php

 

shared

libraries
modules
setttings
templates
workspaces
images
scripts
styles
index.php

 

In the shared hosting setup all directories except images, scripts and styles are protected and can not be accessed through the web.

No because they aren't accessible anyhow because it is under the webroot however on a shared hosting environment you are not allowed to have such setups and everything needs to be contained within the htdocs directory whereby all application directories would be accessible however using .htaccess and chmod you can deny access. Only in a few rare occassions am I forced into this setup.

ok it just looks to me that it's in the dedicated/vps that everything except images, scripts and styles that's protected.. might be wrong tho :)

 

however on a shared hosting environment you are not allowed to have such setups and everything needs to be contained within the htdocs directory whereby all application directories would be accessible however using .htaccess and chmod you can deny access

Ayon asked me some questions in a PM but I'm going to respond here in hopes that:

1) It will help more people

2) I can possibly receive feedback on my ideas from others

 

Your framework should be mostly objects and I don't mean just functions wrapped up in the namespace of an object.

 

what you mean with this? Do you mean that I should make classes like Database, Text, Numbers, Validate, etc?

 

Now regarding the directories and files.. In the two sections you posted you have in the first "class Application" then in the second you've got "class My_Application extends Application" and the same with several other classes... What exactly is the difference between Application and My_Application? Only thing I can think of is that My_Application only contains something like a "loader function" for "Application".... but i'm probably wrong...

 

Before I begin, let me just say two things:

1) Some of these are ideas for the next iteration of the framework I use at work, but not actually implemented.

2) None of this was typed or run in an IDE so excuse typos and don't expect it to work out of the box!

 

I have one, albeit long, answer that takes care of both questions.

 

As I said before, objects are more than collections of functions.  Objects contain behavior.  So let's think for a minute about how we want all of our applications to work.

 

1) We want our applications to have a single entry point: index.php

2) We want our applications to work equally as well from a command line and as an http request.

3) We want a standardized definition of the behavior of an application

4) We want the application to modify the behavior where it needs to

 

Let's start with invoking our application.

 

As a web request, we will need a mod_rewrite rule to filter everything through index.php.  The url will look something like:

http://domain/$CLIENT/$CONTROLLER/$ACTION/$PARAM1/$VALUE1/$PARAM2/$VALUE2?$QUERYSTRING

$CLIENT: I often have to write one application that handles multiple databases.  The $CLIENT portion tells my application which database, configuration, etc. to use.

$CONTROLLER: The business logic handlers of the application.  Examples would be logic for: Users, News, Posts, Headlines, or basically whatever your site is about.

$ACTION: The action to perform on the business item.  Common actions are CRUD: create, read, update, delete, but your application will likely need more.

$PARAMx/$VALUEx pairs:  These are named parameters the same as those in $_GET, however placing them in the URL like this is search engine friendly

$QUERYSTRING: Is everything following the ? in the url, because sometimes it's just necessary.

.htaccess

# Turn on rewriting
RewriteEngine on

# Any request that is an actual file or directory should be served by Apache
# and not filter through index.php.  The following rules represent that.
RewriteCond -f [OR]
RewriteCond -d 
RewriteRule * - [L]

# Everything else is assumed to go through index.php
RewriteRule (.*) index.php [QSA,L]

 

As a console application, we want to type something like the following to invoke our application:

$ pwd
/home/roopurt18/myproject
$ php index.php --client fooclient --controller Import --action RunNow --force true

In that example:

$CLIENT is fooclient

$CONTROLLER is import

$ACTION is run_now

$PARAM1 is force

$VALUE1 is true

 

When invoked, we want index.php to take over and run the application:

index.php

<?php
// Note this file is part of the application, not the framework!

define( 'APP_DIR_HOME', dirname( __FILE__ ) );

// Require the framework, wherever it is on the file system
// bootstrap.php *IS* part of the framework and will load the essential classes
// of the framework, such as: Application, Controller, Layout, View, Request, etc.
require_once( '/path/to/framework/bootstrap.php' );

// Now require *YOUR* application.  This is part of your application
require_once( APP_DIR_HOME . '/private/classes/MyApplication.php' );

// Now run the application with top-level exception handling
try {
$app = new MyApplication();
$app->Run();
}catch( Exception $ex ) {
    MyApplication::LogFatalException( $ex );
}
?>

 

MyApplication.php

<?php
// Here is the basic, non-implemented MyApplication class
class MyApplication extends Application {
}
?>

 

Application.php

<?php
// Here is the application class, which is part of the framework.
abstract class Application {
final public function Run() {
	// Allow the specific application to run initial code
	$this->Startup();

	// Create the request object.  The request object is responsible for
	// determining how the application was run (www or cli) and for
	// providing access to the controller, action, client, parameters, etc.
	$rq = Request::getThisRequest();

	// Allow the specific application a chance to do something pre-dispatch
	$this->PreRequestDispatch();

	// Now dispatch the request to the controller class.  The controller
	// class will now how to instantiate the proper controller.
	$output = Controller::Dispatch( $rq );

	// Allow the specific application a chance to do something post-dispatch
	$this->PostRequestDispatch();

	// Send the output
	echo $output;

	// Allow the specific application to perform special shutdown code
	$this->Shutdown();
}

abstract protected function Startup();
abstract protected function Shutdown();
abstract protected function PreRequestDispatch();
abstract protected function PostRequestDispatch();
} 
?>

 

This isn't a perfect example by any stretch, but hopefully it'll suffice.  Now since Application is abstract and MyApplication implements no functions, PHP will throw errors.

 

My coworker has been giving me grief about abstract classes "because code should work regardless."  But I like that PHP throws errors if I don't implement those functions in MyApplication.  Using abstract allows me to remind myself which pieces of my framework code are required for me to implement.

 

I can't tell you how many times I've written general, reusable code and then tried to reuse it 6 months later.  I always end up forgetting to implement some piece of it, or forget some piece of configuration, etc.  However, the fact that the class is abstract creates a contract and obligation on me when I go to reuse it.  I must fill in those pieces or it will not work.  If I fill in those pieces correctly, then the whole application should just work.

 

So let's fill in MyApplication.php

MyApplication.php

<?php
// Here is the basic, non-implemented MyApplication class
class MyApplication extends Application {
private $_start_tm = false;
private $_end_tm = false;

    protected function Startup() { 
    	// Mark the application start up point
    	$this->_start_tm = microtime( true );
    	
    	// We'll need sessions in this application
    	session_start();
    }
    
    protected function Shutdown() { 
        // Mark the application end time and log to database
        $this->_end_tm = microtime( true );

        $elapsed = $this->_end_tm - $this->_start_tm;
    }
    
    protected function PreRequestDispatch() { /* do nothing */ }
    protected function PostRequestDispatch() { /* do nothing */ }
}
?>

 

Now that I've filled in MyApplication:

1) PHP will stop throwing errors (unless I've introduced some!)

2) The application receives all of the reusable functionality I've built into my framework

3) The framework allows the application to "hook" custom functionality into it at certain points of its execution

 

Now let's look at how some controllers might work.

 

Controller.php

<?php
// Controller.php -- this is part of the framework
abstract class Controller {
protected $_rq; // The request that generated this object

protected function __construct() {
}

// Set the request that generated this object
private function SetRequest( Request $rq ) { $this->_rq = $rq; }

// Dispatches a request
public static function Dispatch( Request $rq ) {
	// The incoming request object knows the controller name (that's its job)
	// But the application knows where the associated class is on disk.
	// So LoadClass() is abstract to the specific application can handle
	// loading the class.
	$this->LoadClass( $rq->GetControllerName() );

	// If the URL is:
	// http://domain/theclient/Users/Add
	$class = $rq->GetControllerName() . 'Controller'; // I.E: Users
	$action = $rq->GetActionName() . 'Action'; // I.E: Add

	// cli requests should not be buffered, all others should
	// Since buffering is on, we can still make header() calls.  
	if( $rq->IsCli() === false ) ob_start();

	$instance = new $class();     // Instantiates: UsersController
	$instance->SetRequest( $rq );
	$instance->{$action}();       // Calls $instance->AddAction()

	// We need the get the buffer contents
	if( $rq->IsCli() === false ) {
		$contents = ob_get_clean();
		return $contents;
	}
	return true; // Cli requests will just dump to the console, but we still
	              // want to signal that they ran, so return true (sort of arbitrary).
}

abstract protected function LoadClass( $name );
} 
?>

 

Now we have the concept of MyController.  We have this for two reasons:

1) Controller is abstract and needs a LoadClass() method.  You only want to implement this method once for all of your controllers.  You don't want to write LoadClass() for your UsersController, NewsController, FoobarController, etc.

2) All of your controllers might need a special functionality.  By creating a MyController you can implement this common functionality here, in one place.

 

MyController.php

<?php
// This is part of your application!
class MyController extends Controller {

// Let's allow all of this applications controller to log their run time
// to a file
private $_start_tm = false;
private $_end_tm = false;

protected function __construct() {
	parent::__construct(); // calls parent

	$this->_start_tm = microtime( true ); // marks start time
}

public function __destruct() {
	$this->_end_tm = microtime( true ); // marks end time

	$fp = fopen( APP_DIR_HOME . '/private/logs/controllers.log' );
	if( $fp ) {
		$fwrite( $fp, $this->_end_tm - $this->_start_tm . " s\n" );
	}
}

    protected function LoadClass( $name ) {
    	// Loads: UsersController, IndexController, NewsController etc
    	require_once( APP_DIR_HOME . '/private/controllers/' . $name . 'Controller.php' );
    }	
} 
?>

 

Now we implement UsersController.

UsersController.php

<?php 
// This is part of your application
class UsersController extends MyController {
protected function __construct() {
	parent::__construct();
}

public function AddAction() { /* add a user */ }

public function DeleteAction() { /* delete a user */ }
}
?>

 

So with that model we have the following:

1) Controller is part of the framework and handles base functionality.  My example doesn't do much, but a more evolved one might load the View, instantiate database objects, etc.

2) MyController allows us to use all of the behavior defined in Controller and add custom functionality to each of the application's controllers.

3) The final controllers that are instantiated (UsersController, NewsController, etc.) will all have:

  a) A Request object, a View object, i.e. stuff common to every application

  b) Time logging to a file without any extra effort per controller, they all just receive the behavior.

 

So that's a bit long winded but I hope it helps and I'm looking forward to any feedback.

 

I want to reiterate the code samples are merely that.  They're not wonderfully designed because I'm tired and I'm demonstrating concepts, not building a public framework.

 

Now here's the challenge:  I want you to tell me how you can do all of that with a library of functions you copy and paste from project to project.

wow thanks for a totally amazing answer! i now see the reason why i've been so "scared" of digging into making a framework... but i will face my fear now and start working on one...

 

I do really appreciate you taking your time writing such a detailed answer to my questions.. _O_

If you're going to set off on creating a reusable framework for yourself and you have the time, I suggest the following:

 

Don't think so much about how you should write the framework, think more about how you want your application to interact with it.  Let your application needs dictate how you write and organize the framework.

 

Move in small chunks.

 

Start with index.php and how you want to load the framework and kickstart everything into action.  Write the application code first and go ahead and use classes and methods that don't exist yet.  Then go implement those classes and methods in your framework.  Get it working with a simple, sample application.

 

Then create one or two more simple applications and get them to the same stage of completion as your first sample project.  Was your framework easy to use?  Is there one thing you're constantly typing in each application that you could move into the framework?  Redesign your framework to accommodate any changes you want to make now.

 

Think about how you really want everything to work.

 

Here's an example of a design decision I made when creating the framework I use at work.  The url http://domain.com/Foo/Add will map to the FooController::Add method.  This is magically handled by my framework.  But it's reasonable to assume that controllers will have methods not meant to be mapped to urls.  I don't want someone to fiddle with a URL to try and discover methods in my controller.

 

To accommodate this, I decided that all methods publicly mapped to URLs should have the word "Action" appended to them.  Therefore my framework will actually map http://domain.com/Foo/Add to FooController::AddAction().  This allows the following:

 

<?php
class FooController extends MyController {
    public function __construct() { parent::__construct(); }

    public function AddAction() { /* publicly available via URL */ }

    private function add_helper() { /* method is private and name does not end in 'Action'.  Therefore not available via URL */ }
}

?>

 

:)

got one more question for you... on a shared how.. how would the directory and file setup look like?

 

I tried to cover this in my first post: http://www.phpfreaks.com/forums/index.php/topic,284048.msg1346941.html#msg1346941

 

I've also attached a sample junk.zip file with the directory structure for a sample application and potential framework.

 

[attachment deleted by admin]

yet again i really wanna thank you for taking your time to describe this so carefully and detailed man... i know I'm really learning alot of extremely useful things from this, and I'm sure alot of other people also finds your replies very useful!

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.