Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>In response to Sébastien Renauld's comments: <a href="http://phpmaster.com/inversion-of-control-the-hollywood-principle/" rel="nofollow">here's an article on Dependency Injection (DI) and Inversion of Control (IoC)</a> with some examples, and a few extra words on the Hollywood principle (quite important when working on a framework).</p> <p>Saying your classes won't ever need more than a single instance doesn't mean that statics are a must. Far from it, actually. If you browse this site, and read through PHP questions that deal with the singleton <em>"pattern"</em>, you'll soon find out why singletons are a bit of a no-no.</p> <p>I won't go into the details, but testing and singletons don't mix. Dependency injection is definitely worth a closer look. I'll leave it at that for now.</p> <p>To answer your question:<br/> Your exaple (<code>Config::get('test')</code>) implies you have a static property in the <code>Config</code> class somewhere. Now if you've done this, as you say, to facilitate access to given data, imagine what a nightmare it would be to debug <em>your</em> code, if that value were to change somewhere... It's a static, so change it once, and it's changed everywhere. Finding out where it was changed might be harder than you anticipated. Even so, that's <em>nothing</em> compared to the issues someone who <em>uses</em> your code will have in the same situation.<br/> And yet, the <em>real</em> problems will only start when that person using your code wants to test whatever it is he/she made: If you want to have access to an instance in a given object, that has been instantiated in some class, there are plenty of ways to do so (especially in a framework):</p> <pre><code>class Application {//base class of your framework private $defaulDB = null; public $env = null; public function __construct($env = 'test') { $this-&gt;env = $env; } private function connectDB(PDO $connection = null) { if ($connection === null) { $connection = new PDO();//you know the deal... } $this-&gt;defaultDB = $connection; } public function getDB(PDO $conn = null) {//get connection if ($this-&gt;defaultDB === null) { $this-&gt;connectDB($conn); } return $this-&gt;defaultDB; } public function registerController(MyConstroller $controller) {//&lt;== magic! $controller-&gt;registerApplication($this); return $this; } } </code></pre> <p>As you can see, the <code>Application</code> class has a method that passes the <code>Application</code> instance to your controller, or whatever part of your framework you want to grant access to scope of the <code>Application</code> class.<br/> Note that I've declared the <code>defaultDB</code> property as a private property, so I'm using a getter. I can, if I wanted to, pass a connection to that getter. There's a lot more you can do with that connection, of course, but I can't be bothered writing a full framework to show you everything you can do here :).</p> <p>Basically, all your controllers will extend the <code>MyController</code> class, which <em>could</em> be an abstract class that looks like this:</p> <pre><code>abstract class MyController { private $app = null; protected $db = null; public function __construct(Application $app = null) { if ($app !== null) { return $this-&gt;registerApplication($app); } } public function registerApplication(Application $app) { $this-&gt;app = $app; return $this; } public function getApplication() { return $this-&gt;app; } } </code></pre> <p>So in your code, you can easily do something along the lines of:</p> <pre><code>$controller = new MyController($this);//assuming the instance is created in the Application class $controller = new MyController(); $controller-&gt;registerApplication($appInstance); </code></pre> <p>In both cases, you can get that single DB instance like so:</p> <pre><code>$controller-&gt;getApplication()-&gt;getDB(); </code></pre> <p>You can test your framework with easily by passing a different DB connection to the <code>getDB</code> method, if the <code>defaultDB</code> property hasn't been set in this case. With some extra work you can register multiple DB connections at the same time and access those at will, too:</p> <pre><code>$controller-&gt;getApplication-&gt;getDB(new PDO());//pass test connection here... </code></pre> <p>This is, by no means, the full explanation, but I wanted to get this answer in quite quickly before you end up with a huge static (and thus useless) codebase.</p> <p><em>In response to comments from OP:</em></p> <p>On how I'd tackle the <code>Config</code> class. Honestly, I'd pretty much do the same thing as I'd do with the <code>defaultDB</code> property as shown above. But I'd probably allow for more targeted control on what class gets access to what part of the config:</p> <pre><code>class Application { private $config = null; public function __construct($env = 'test', $config = null) {//get default config path or use path passed as argument $this-&gt;config = new Config(parse_ini_file($config)); } public function registerController(MyController $controller) { $controller-&gt;setApplication($this); } public function registerDB(MyDB $wrapper, $connect = true) {//assume MyDB is a wrapper class, that gets the connection data from the config $wrapper-&gt;setConfig(new Config($this-&gt;config-&gt;getSection('DB'))); $this-&gt;defaultDB = $wrapper; return $this; } } class MyController { private $app = null; public function getApplication() { return $this-&gt;app; } public function setApplication(Application $app) { $this-&gt;app = $app; return $this; } //Optional: public function getConfig() { return $this-&gt;app-&gt;getConfig(); } public function getDB() { return $this-&gt;app-&gt;getDB(); } } </code></pre> <p>Those last two methods aren't really required, you could just as well write something like:</p> <pre><code>$controller-&gt;getApplication()-&gt;getConfig(); </code></pre> <p>Again, this snippet is all a bit messy and incomplete, but it does go to show you that you can <em>"expose"</em> certain properties of one class, by passing a reference to that class to another. Even if the properties are private, you can use getters to access them all the same. You can also use various register-methods to control what it is the registered object is allowed to see, as I've done with the DB-wrapper in my snippet. A DB class shouldn't deal with viewscripts and namespaces, or autoloaders. That's why I'm only registering the DB section of the config.</p> <p>Basically, a lot of your main components will end up sharing a number of methods. In other words, they'll end up implementing a given interface. For each main component (assuming the classic MVC pattern), you'll have one abstract base-class, and an inheritance chain of 1 or 2 levels of child classes: <code>Abstract Controller</code> > <code>DefaultController</code> > <code>ProjectSpecificController</code>.<br/> At the same time, all of these classes will probably expect another instance to be passed to them when constructed. Just look at the <code>index.php</code> of any ZendFW project:</p> <pre><code>$application = new Zend_Application(APPLICATION_ENV); $application-&gt;bootstrap()-&gt;run(); </code></pre> <p>That's all you can see, but inside the application, all other classes are being instantiated. That's why you can access neigh on everything from anywhere: all classes have been instantiated inside another class along these lines:</p> <pre><code>public function initController(Request $request) { $this-&gt;currentController = $request-&gt;getController(); $this-&gt;currentController = new $this-&gt;currentController($this); return $this-&gt;currentController-&gt;init($request) -&gt;{$request-&gt;getAction().'Action'}(); } </code></pre> <p>By passing <code>$this</code> to the constructor of a controller class, that class can use various getters and setters to get to whatever it needs... Look at the examples above, it could use <code>getDB</code>, or <code>getConfig</code> and use that data if that's what it needs.<br/> That's how most frameworks I've tinkered or worked with function: The application is kicks into action and determines what needs to be done. That's the Hollywood-principle, or Inversion of Control: the Application is started, and the application determines what classes it needs when. In the link I provided I believe this is compared to a store creating its own customers: the store is built, and decides what it wants to sell. In order to sell it, it will create the clients it wants, and provide them with the means they need to purchase the goods...</p> <p>And, before I forget: Yes, all this can be done without a single static variable, let alone function, coming into play. I've built my own framework, and I've never felt there was no other way than to <em>"go static"</em>. I did use the Factory pattern at first, but ditched it pretty quickly. <Br/> IMHO, a good framework is modular: you should be able to use bits of it (like Symfony's components), without issues. Using the Factory pattern makes you <em>assume</em> too much. You <em>assume</em> class X will be available, which isn't a given.<Br/> Registering those classes that are available makes for far more portable components. Consider this:</p> <pre><code>class AssumeFactory { private $db = null; public function getDB(PDO $db = null) { if ($db === null) { $config = Factory::getConfig();//assumes Config class $db = new PDO($config-&gt;getDBString()); } $this-&gt;db = $db; return $this-&gt;db; } } </code></pre> <p>As opposed to:</p> <pre><code>class RegisteredApplication {//assume this is registered to current Application public function getDB(PDO $fallback = null, $setToApplication = false) { if ($this-&gt;getApplication()-&gt;getDB() === null) {//defensive if ($setToApplication === true &amp;&amp; $fallback !== null) { $this-&gt;getApplication()-&gt;setDB($fallback); return $fallback;//this is current connection } if ($fallback === null &amp;&amp; $this-&gt;getApplication()-&gt;getConfig() !== null) {//if DB is not set @app, check config: $fallback = $this-&gt;getApplication()-&gt;getConfig()-&gt;getSection('DB'); $fallback = new PDO($fallback-&gt;connString, $fallback-&gt;user, $fallback-&gt;pass); return $fallback; } throw new RuntimeException('No DB connection set @app, no fallback'); } if ($setToApplication === true &amp;&amp; $fallback !== null) { $this-&gt;getApplication()-&gt;setDB($fallback); } return $this-&gt;getApplication()-&gt;getDB(); } } </code></pre> <p>Though the latter version is slightly more work to write, it's quite clear which of the two is the better bet. The first version just assumes too much, and doesn't allow for safety-nets. It's also quite dictatorial: suppose I've written a test, and I need the results to go to another DB. I therefore need to change the DB connection, for the entire application (user input, errors, stats... they're all likely to be stored in a DB).<br/> For those two reasons alone, the second snippet is the better candidate: I can pass another DB connection, that overwrites the application default, or, if I don't want to do that, I can either use the default connection, or attempt to create the default connection. Store the connection I just made, or not... the choice is entirely mine. If nothing works, I just get a <code>RuntimeException</code> thrown at me, but that's not the point.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload