2

# Introduction

I am working on a CMS application in PHP with about 200 classes. The CMS, in general, does the same thing every CMS does: generate sites.

I am learning a lot about OOP and design patterns and only recently started learning about how to create maintainable code using Dependency Injection.

The issue is that I don't know how to solve the issues I currently have and with which design pattern.

# Possible solutions

I have read of the following (possible) solutions:

  • Service Provides
  • Dependency Injection Containers
  • Factory Classes
  • Dependency Injection
  • Etc...

All the solutions seem to have advantages and disadvantages, but the more I read about them, the more difficulties I have choosing one.

# The problem

The biggest issue is needing stateful dependency, like page or site which contain data loaded from the database, in an object nested inside other classes.

To explain this further, I think it is best to show you an example. The flow of the system is something like this:

  1. Every request is redirected to the index.php file using the rewrite module of Apache
  2. The index.php script initiates the app class and calls the function start_app()
  3. The start_app() function dissects the URL and uses GET/POST variables to generate a route to a certain page
  4. Every dependency that is loaded is contained in a dependencyLoader
  5. When all dependencies are loaded, a template file is parsed and each line is checked for specific tags (e.g. <!-- CSS-FILE --!>) and data from the dependencies is then rendered into HTML and placed instead of the tag.
  6. If a tag is found, the dependencies are asked if it contains data for this specific tag and will return specific objects that render their data in a unique way. E.g. objects like 'phpInclude', 'cssFile', 'element', 'metaData' etc..

The app class looks something like this (simplified):

     class app {

        protected $dependencyContainer;

        function __construct() {
            $this->dependencyContainer = new dependencyContainer();
        }

        public function start_app() {

            // Load in navigator that contains routing information
            $this->dependencyContainer->set('navigator', new navigator($_SERVER['REQUEST_URI'], $_SERVER['HTTP_HOST']));

            // Load site data 
            $domainController = new domainController();
            $site = $domainController->loadSite($this->dependencyContainer->get('navigator'));
            $this->dependencyContainer->set('site', $site);

            $pageController = new pageController();
            $page = $pageController->loadPage($this->dependencyContainer->get('navigator'));
            $this->dependencyContainer->set('page', $page);

            // Etc...
            // Etc...

            $templatePath = $site->getTemplate();
            $this->renderDocument($templatePath);
        }

        private function renderDocument($templatePath) {       
            $template = file_get_contents($templatePath);

            $lines = explode("\n", $template);

            foreach($lines as $line) {                    
                // If a tag is found, the dependencies are asked if it contains data for this 
                if($tag = $this->filter($line)) {
                    $fragments = $this->dependencyContainer->output($tag);

                    foreach($fragment as $fragment) {
                        if($fragment->needDependencies()) {
                            $fragment->render($this->dependencyContainer);
                        } else {
                            $fragment->render();
                        }
                    }
                // Process lines, echo the ones that are plain HTML
                } else {
                    echo $line;
                }
             }
        }
    }

The returned objects (fragments) can be of two types:

elementFragment {
     protected $data;
     const needDependencies = false;

     public render() {
          // Render logic here
          return 'SOME HTML';
     }
}

dependencyFragment {
     protected $data;
     const needDependencies = true;

     public render($dependencies) {
          if($dependecies->has('page')) $page = $dependencies->get('page');
          if($dependecies->has('site')) $site = $dependencies->get('site');
          // Etc etc etc etc etc

          // Logic to generate HTML here
          // E.g the phpInclude fragments has logic like this:
          return include __PATH__ . $this->data;
     }
}

The issue arises when I have another level of objects inside the dependencyFragment object. This is sadly a common situation.

# Questions

  • Do I keep pushing the dependencyContainer into every function call or object constructor?
  • Or maybe a (static) singleton pattern is a better solution to house the dependencies and make them available?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.