SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    SitePoint Zealot
    Join Date
    Jun 2003
    Location
    Elsewhere
    Posts
    107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    somersault: a configuration driven framework

    Edit:

    Re-added somersault.zip to this post, after spybreak was kind enough ot point out a bug in smsRequest_HTTP.php


    Well, here it is...

    I promised a couple of people that I'd post my code on these forums, so here goes.

    There are a few disclaimers regarding my framework though:
    • I've made some major changes during the past weekend, renaming more than half my classes, removing and modifying quite a number of features. The code should be stable, but there are still some areas which need refactoring/restructuring.
    • This code only works with PHP5.
    • I use tabs for indentation. I know: coding standards, blah, blah. Code looks best with tabspacing = 3.
    • What I'm posting here is the framework on which I'm building my own application. Personally, I don't think frameworks are much use, because they either do too little or too much. My framework doesn't do anything, apart from offering you a highly decoupled application environment.
    • The attached file contains a 'Hello, world!' example. This example is a great example of building a very simple feature using an overkill architecture. The real application will be rather more complex, including multiple databases, websites, client types, and whatnot, all running on a single installation (but not necessarily the same server).
    • In the end, devices, components, and sites will be created by the application itself, using install scripts (similar to make files). At the moment developers will need to modify the device files manually.
    • The example code already offers you the ability to install multiple sites using the same installation of the framework. I've just packed everything into the same directory to make installation easier. If you're thinking of creating multiple sites, just ask how to do it.


    Installation:

    Unzip into a subdirectory of your webroot. It should work.

    Quick introduction:
    • The modules directory contains all the source code, and defines which features you can potentially add to one of the sites.
    • The components directory defines the available features of a site; each configuration file in a subdirectory points to a source files in the modules tree.
    • Devices (or device objects) are a combination of a configuration file from the components tree, and a class from the modules tree. The class can be used by many devices, the configuration file is the unique identifier.
    • Whenever a device is instantiated, the somersault class passes it its configuration through the __construct() method.


    Anywho, here is the code. Enjoy.

    /framework/somersault.php
    PHP Code:
    <?php
    /******************************************************

       Copyright (C) 2004 Azmo

     This software is released under the LGPL, or Lesser
     General Public License. For more information visit
     [url]www.gnu.org/licenses/lgpl.html[/url]

     This library is distributed in the hope that it will
     be useful, but WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.

    ******************************************************/

    class somersault
    {
        protected static 
    $device;
        protected static 
    $site_path;

        protected final function 
    __construct() {}

        public static function 
    setSitePath$sitePath )
        {
            if ( isset( 
    self::$site_path ) )
            {
                throw new 
    smsError'Variable \'somersault::$site_path\' is already set.''error' );
            }

            
    self::$site_path $sitePath;
        }

        public static function 
    getDevice$deviceData$errorLevel 'warning' )
        {
            if ( isset( 
    self::$device$deviceData->component ][ $deviceData->name ] ) )
            {
                return( 
    self::$device$deviceData->component ][ $deviceData->name ] );
            }

            
    $config_file "components/{$deviceData->component}/{$deviceData->name}.php";

            
    self::verifyFileself::$site_path$config_file$errorLevel );

            require_once( 
    self::$site_path $config_file );

            
    $device_data    = new smsNestedHash$device['data'] );

            
    $device_class    $device_data->self->class;



            if ( !
    class_exists$device_class ) )
            {
                
    $device_module        $device_data->self->module;

                
    $class_file            'modules/'$device_module .'/devices/'$device_class .'.php';

                
    self::verifyFileSMS_PATH$class_file );

                require_once( 
    SMS_PATH $class_file );
            }

            if ( !
    class_exists$device_class ) )
            {
                throw new 
    smsError"Class '{$device_class}' does not exist."'error' );
            }

            
    // Create the new device
            
    $device_instance = new $device_class$device_data );

            
    self::$device$deviceData->component ][ $deviceData->name ] = $device_instance;

            return( 
    $device_instance );
        }

        public static function 
    verifyFile$basePath$fileName$errorLevel 'warning' )
        {
            if ( !
    file_exists$basePath$fileName ) )
            {
                throw new 
    smsError"File '.../{$fileName}' does not exist."$errorLevel );
            }
            elseif ( !
    is_readable$basePath$fileName ) )
            {
                throw new 
    smsError"File '.../{$fileName}' is not readable."$errorLevel );
            }
        }
    }

    ?>
    /framework/smsDevice.php
    PHP Code:
    <?php
    /******************************************************

       Copyright (C) 2004 Azmo

     This software is released under the LGPL, or Lesser
     General Public License. For more information visit
     [url]www.gnu.org/licenses/lgpl.html[/url]

     This library is distributed in the hope that it will
     be useful, but WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.

    ******************************************************/

     
    class smsDevice
    {
        protected 
    $device_data;

        public function 
    __construct$deviceData )
        {
            
    $this->device_data        $deviceData;
        }

        public function 
    __call$methodName$arguments )
        {
            throw new 
    smsError"Device does not have a method called '{$methodName}'."'error' );
        }

        protected function 
    getDevice$deviceName$errorLevel 'warning' )
        {
            if ( 
    $this->device_data->device && $this->device_data->device->$deviceName )
            {
                
    $device_data    $this->device_data->device->$deviceName;
            }
            else
            {
                
    $device_data    = new smsNestedHash();
            }

            if ( !
    $device_data->name )
            {
                
    $device_data->name $deviceName;
            }

            if ( !
    $device_data->component )
            {
                
    $device_data->component $this->device_data->self->component;
            }

            
    $device_instance            somersault::getDevice$device_data$errorLevel );

            return( 
    $device_instance );
        }

        
    /// Request-related parameter processing ///

        
    protected $Request;

        protected function 
    getParameters$allowMissing false )
        {
            
    $this->Request    $this->getDevice'request' );

            
    $parameter $this->device_data->param;

            return( 
    $this->Request->get$parameter$allowMissing ) );
        }

        
    /// Client access verification ///

        
    protected function checkAccess()
        {
            
    //? Note: Azmo 20040514: Not implemented yet.
        
    }

    }

    ?>
    /framework/smsNestedHash.php
    PHP Code:
    <?php
    /******************************************************

       Copyright (C) 2004 Azmo

     This software is released under the LGPL, or Lesser
     General Public License. For more information visit
     [url]www.gnu.org/licenses/lgpl.html[/url]

     This library is distributed in the hope that it will
     be useful, but WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.

    ******************************************************/

    class smsNestedHash
    {
        protected 
    $__value = array();

        public function 
    __construct$values = array() )
        {
            foreach( 
    $values as $name => $value )
            {
                
    $this->__set$name$value );
            }
        }

        public function 
    __get$keyName )
        {
            if ( isset( 
    $this->__value$keyName ] ) )
            {
                return( 
    $this->__value$keyName ] );
            }

            return( 
    false );
        }

        public function 
    __set$keyName$value )
        {
            if ( 
    is_array$value ) )
            {
                
    $this->__value$keyName ] = new smsNestedHash$value );
            }
            else
            {
                
    $this->__value$keyName ] = $value;
            }
        }

        public function 
    getAll()
        {
            return( 
    $this->__value );
        }

        public function 
    importsmsNestedHash $nestedHash )
        {
            
    $values $nestedHash->getAll();

            foreach( 
    $values as $name => $value )
            {
                
    $this->__set$name$value );
            }
        }

        public function 
    export()
        {
            
    $result = array();

            foreach( 
    $this->__value as $name => $value )
            {
                if ( 
    $value instanceof smsNestedHash )
                {
                    
    $result$name ] = $value->export();
                }
                else
                {
                    
    $result$name ] = $value;
                }
            }

            return( 
    $result );
        }
    }

    ?>
    /framework/smsError.php
    PHP Code:
    <?php
    /******************************************************

       Copyright (C) 2004 azmo

     This software is released under the LGPL, or Lesser
     General Public License. For more information visit
     [url]www.gnu.org/licenses/lgpl.html[/url]

     This library is distributed in the hope that it will
     be useful, but WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.

    ******************************************************/

    class smsError extends Exception
    {
        protected 
    $level;
        protected 
    $class;
        protected 
    $method;
        protected 
    $type;

        public function 
    __construct$errorMessage$errorLevel )
        {
            
    parent::__construct$errorMessage );

            
    $stack_trace $this->getTrace();

            
    $this->level        $errorLevel;
            
    $this->class        $stack_trace[0]['class'];
            
    $this->method        $stack_trace[0]['function'];
            
    $this->type            $stack_trace[0]['type'];
        }

        public function 
    getLevel()
        {
            return( 
    $this->level );
        }

        public function 
    getMethod()
        {
            return( 
    $this->method );
        }

        public function 
    __toString()
        {
            return( 
    $this->class .$this->type$this->method .'() threw a '$this->level .': '$this->message );
        }
    }

    ?>
    /modules/core/devices/smsAction_GetBlock.php
    PHP Code:
    <?php
    /******************************************************

       Copyright (C) 2004 Azmo

     This software is released under the LGPL, or Lesser
     General Public License. For more information visit
     [url]www.gnu.org/licenses/lgpl.html[/url]

     This library is distributed in the hope that it will
     be useful, but WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.

    ******************************************************/

    class smsAction_GetBlock extends smsDevice
    {
        public function 
    perform()
        {
            
    $block_name            $this->getBlockName();

            
    $Output                $this->getDevice'output' );

            
    $output_data        $this->device_data->output;

            
    $output_data->block = array(
                
    'main' => array(
                    
    'component' => $this->device_data->self->component,
                    
    'name' => $block_name));

            
    $Output->process$output_data );
        }

        protected function 
    getBlockName()
        {
            if ( 
    $this->device_data->block->fixed )
            {
                
    $block_name $this->device_data->block->fixed;
            }
            else
            {
                
    $parameter            $this->getParameterstrue );

                
    $block_name            $parameter->block;

                if (    
    $this->device_data->block->default &&
                        
    false === $block_name )
                {
                    
    $block_name $this->device_data->block->default;
                }
                elseif ( 
    $this->device_data->block->allowed )
                {
                    
    $allowed $this->device_data->block->allowed->export();

                    if ( !
    in_array$block_name$allowed ) )
                    {
                        throw new 
    smsError"Requested block '{$block_name}' not allowed."'warning' );
                    }
                }
                elseif ( 
    $this->device_data->block->disallowed )
                {
                    
    $disallowed $this->device_data->block->disallowed->export();

                    if ( 
    in_array$block_name$disallowed ) )
                    {
                        throw new 
    smsError"Requested block '{$block_name}' disallowed."'warning' );
                    }
                }
            }

            return( 
    $block_name );
        }
    }

    ?>
    /components/example/action_getblock.php
    PHP Code:
    <?php

    $device
    ['data'] = array(
        
    'self' => array(
            
    'name' => 'action_getblock',
            
    'component' => 'example',
            
    'module' => 'core',
            
    'class' => 'smsAction_GetBlock'),
        
    'device' => array(
            
    'request' => array(
                
    'component' => 'core',
                
    'name' => 'request_http'),
            
    'output' => array(
                
    'component' => 'core',
                
    'name' => 'output_template')),
        
    'block' => array(
            
    'default' => 'hello_world'),
        
    'param' => array(
            
    'block' => array(
                
    'filter' => 'word')),
        
    'output' => array(
            
    'component' => 'example',
            
    'template' => 'page_main',
            
    'doctype' => 'html'));

    ?>
    /components/core/output_template.php
    PHP Code:
    <?php

    $device
    ['data'] = array(
        
    'self' => array(
            
    'name' => 'output_template',
            
    'component' => 'core',
            
    'module' => 'core',
            
    'class' => 'smsOutput_Template'),
        
    'config' => array(
            
    'site_path' => SMS_PATH));

    ?>
    Here's how one device gets access to another (see smsAction_GetBlock.php, action_getblock.php, and output_template.php):
    • smsAction_GetBlock calls $this->getDevice( 'output' )
    • smsDevice::getDevice() looks for a device entry called 'output' in the device data, and finds it (component = core, name = output_template).
    • smsDevice::getDevice() calls somersault::getDevice() with the data describing the required device.
    • somersault::getDevice() looks for /components/core/output_template.php and reads it.
    • somersault::getDevice() looks at the module and class name entries, then reads /modules/core/smsOutput_Template.php
    • somersault::getDevice() creates a new smsOutput_Template object, passing the data from output_template into the constructor.
    • Device object is returned to smsAction_GetBlock.


    That's all there is to it...

    Edit: source file removed
    Last edited by Azmo; Apr 14, 2006 at 10:14.

  2. #2
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks Azmo, I was one of many who was interested in learning more about your framework

    Going to take a look at your attachment later this week. I won't be able to test it though since I don't have a box with PHP version 5

    But would be intresting to see how it works out for other members though

  3. #3
    SitePoint Zealot
    Join Date
    Jun 2003
    Location
    Elsewhere
    Posts
    107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    No-one else is interested?

    Maybe I should list a couple of somersault's advantages:

    * Nearly unlimited components per site
    There's no actual limit on the number of components in a single site, apart from the number of useful component names you can think of.

    * Unlimited sites
    The application can be separated into three elements: framework + modules, components + init script, and www directory. By creating copies of the last two elements, you can run as many somersault based sites on your server as you like, all using the same source code in the framework + modules directory. This separation will also allow you to keep all configuration and source code out of the webtree.

    * No forced layering
    In theory, each device can fetch an instance of every other device that's available within a site. Just add a $this->getDevice( 'device_name' ) call in your device class, add an entry in the correct device config file, and you're set.

    Example: there is no need to create a database object in a high layer, and then pass it down to where it's actually needed. Every device can fetch whichever other devices it needs to function. This way you can completely hide the actual implementation of lower layers from the higher layers of objects.

    * Fully localized application knowledge
    Each device's configuration contains all the data it needs to perform its job. You don't need to tell a DB device which user name and password to use: it already knows. If you want to change your database details, all you have to change is the database device's config.

    Future plans include:
    • Automated installation of components.
    • Fullblown group-based access control.
    • Output XHTML, XML, RSS, text, PDF, ...
    • Support for multiple types of client (users + remote sites).
    • And much more...

  4. #4
    SitePoint Guru
    Join Date
    Oct 2001
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Some comments:

    Nearly unlimited components per site
    what's your definition of components? The word is (mis)used so much that it has any meaning anyone wants it to have. A site can have unlimted pages, even unlimited HTML tags and you could describe all of that as unlimited components.

    Every device can fetch whichever other devices it needs to function. This way you can completely hide the actual implementation of lower layers from the higher layers of objects.
    No forced layering == no layering at all

  5. #5
    SitePoint Zealot
    Join Date
    Jun 2003
    Location
    Elsewhere
    Posts
    107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Captain Proton
    what's your definition of components? The word is (mis)used so much that it has any meaning anyone wants it to have. A site can have unlimted pages, even unlimited HTML tags and you could describe all of that as unlimited components.
    Example components: guestbook, bulletin board, article section, wiki, admin CP, user profile, member list, poll, PM system, etcetera. It's important to understand that 'nearly unlimited components' doesn't just refer to the different components I'm planning to build. It's possible to create several admin CPs on the same site (probably rather pointless) or multiple, separate bulletin boards, wikis, or whatever.

    Maybe I should have called them site features, which is an equally undescriptive name.

    Quote Originally Posted by Captain Proton
    No forced layering == no layering at all
    This is a rather cryptic comment, and I'm afraid I'm not entirely sure what you mean. Can you elaborate?

  6. #6
    SitePoint Guru
    Join Date
    Oct 2001
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is a rather cryptic comment, and I'm afraid I'm not entirely sure what you mean. Can you elaborate?
    The point of layering is to make different parts of your code (usually in the form of classes) loosely coupled to eachother. When you do not enforce this layering, in other words when you allow high layers to skip other layers to achieve a task, the layering disappears because the code in higher layers becomes coupled to the lower layers.

  7. #7
    SitePoint Zealot
    Join Date
    Jun 2003
    Location
    Elsewhere
    Posts
    107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Captain Proton
    The point of layering is to make different parts of your code (usually in the form of classes) loosely coupled to eachother. When you do not enforce this layering, in other words when you allow high layers to skip other layers to achieve a task, the layering disappears because the code in higher layers becomes coupled to the lower layers.
    Ok, now I get what you mean.

    You're right, this danger is real. But IMO it's up to the developers to decide how their application should be structured. And I don't believe you can prevent a developer from willfully breaking layering, even if you're "enforcing" it. You can't prevent people from including the database class, creating a database handler, and executing queries inside a controller they coded themselves (assuming for a second that a controller should not communicate with a database directly). So we have to rely on the abilities and judgement of the people building on our work.

    I believe that if you care about building a good structure for your application, and the architecture doesn't oppose you, then some sort of layering will emerge naturally. And the whole point of somersault's architecture is to remove as much direct coupling as possible.

  8. #8
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm interested in checking it out. Thanks for posting.

    JT

  9. #9
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Azmo
    Ok, now I get what you mean.

    You're right, this danger is real. But IMO it's up to the developers to decide how their application should be structured. And I don't believe you can prevent a developer from willfully breaking layering, even if you're "enforcing" it. You can't prevent people from including the database class, creating a database handler, and executing queries inside a controller they coded themselves (assuming for a second that a controller should not communicate with a database directly). So we have to rely on the abilities and judgement of the people building on our work.

    I believe that if you care about building a good structure for your application, and the architecture doesn't oppose you, then some sort of layering will emerge naturally. And the whole point of somersault's architecture is to remove as much direct coupling as possible.
    Azmo,

    I'm going to check your framework tonight. I just wanted to say that the main reason I don't like frameworks in general is because they force you into structuring/layering your site a certain way. Which can be OK, but more times than not I needed to get out of that structure to do something different (in a more practical manner) and usually requires some kind of hack and at the very least break the 'structure' of the framework. I like your philosophy on framework structuring (loose. I'll let get back to you about my experiences with your framework!

    Matt

  10. #10
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Azmo,

    Do you have any documentation for Somersault? Do you have any updates? If you don't have any documentation, would you mind explaining how the whole framework 'works'? I've checked it out a bit tonight already, but I think I'd get it much quicker if you could just describe it.

    Thanks!

    Matt

  11. #11
    SitePoint Zealot
    Join Date
    Jun 2003
    Location
    Elsewhere
    Posts
    107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by mwmitchell
    Azmo,

    Do you have any documentation for Somersault? Do you have any updates? If you don't have any documentation, would you mind explaining how the whole framework 'works'? I've checked it out a bit tonight already, but I think I'd get it much quicker if you could just describe it.

    Thanks!

    Matt
    I never planned to release any source this early; I only did so because people were interested. So the somersault framework is still quite unstable (as far as structure is concerned) and there aren't nearly enough modules completed to merit a public release. Right now, there is no documentation, and there aren't any tests (I do have a decent debugger/tracker working). I'm planning to build my own testing functionality, based on configuration files, and auto-build documentation from the code and tests. This will take a while though...

    You're right about frameworks; most of them are either too limited or too limiting. But somersault is a special framework: it's only really intended to run one application (somersault ), which can exist of a great many modules. The source code included in the example is a completely stripped-down version of what I'm working on, and the example itself is pathetically simplistic. But somersault is intended to build arrays of complex sites on a single host, with different types of, and partially shared, data sources. So the example doesn't really do somersault justice...

    As far as somersault's architecture is concerned: at the very bottom of the first post in this thread there's a short description of how one 'device' gains access to another. That short example describes how the entire application is glued together.

    If there's enough interest, I can release another example based on the new layout of somersault somewhere next week. It should be a bit easier to understand, since I've completely done away with the idea of 'components'. Using the new structure, each site can only have one instance of a module running, but it's now possible to override any template, device, or class by creating a replacement in your site directory.

  12. #12
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If there's enough interest, I can release another example based on the new layout of somersault somewhere next week. It should be a bit easier to understand, since I've completely done away with the idea of 'components'. Using the new structure, each site can only have one instance of a module running, but it's now possible to override any template, device, or class by creating a replacement in your site directory.
    Please do It all sounds btw


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •