Instantiation of a PHP class without assignment to a variable - php

I came a cross this line of code in Codeigniter HMVC extension (by Wiredesignz), where a class got instantiated without getting assigned to a variable (class CI in Base.php)
The code :
class CI extends CI_Controller
{
public static $APP;
public function __construct() {
/* assign the application instance */
self::$APP = $this;
global $LANG, $CFG;
/* re-assign language and config for modules */
if ( ! is_a($LANG, 'MX_Lang')) $LANG = new MX_Lang;
if ( ! is_a($CFG, 'MX_Config')) $CFG = new MX_Config;
parent::__construct();
}
}
/* create the application object */
new CI;
What's the name of this technique?
What's the implication?

This has not a name and the implication is, that the constructor is definitely doing too much. The reason one wants to create an instance of a class without referencing it is, that he only wants the constructor wants to be executed, but nothing more. This means, that the constructor "does" something, but a constructor should only ensure, that an object is in a stable/valid state and nothing more.
In short: Don't assume that this is a good practice. The global and self::$APP = $this confirms my opinion that this is a bad piece of code.

I guess this could be seen as some sort of facade design.
-The Class(constructor is called) and assignment is done, albeit in the constructor itself.
so new CI is just extending the Super object and initializing its own constructor.
Similar to a function, function somefun(){ return }; somefunc();//call somefunc
CI_Controller loads all of the classes required to run Codeigniter, it is the SUPER object
$ci = &get_instance() // CI_Controller

Related

How to access other classes methods within a class (PHP)

I'm building a PHP OOP MVC Framework for personal use.
I'd like to know if there's a way or the correct implementation of the following:
Being able to call a method from a "subclass" (not extended) to another "subclass". I mean...
I have a class that creates new objects for each subclass instead of using inheritance. Let's call it MainClass, and it has 3 "SubClasses" like this
class MainClass {
public $db;
public $forms;
public $pagination;
function __construct() {
$this->db = new class_db();
$this->forms = new class_forms();
$this->utils = new class_utils();
}
}
And the initialization which is
$MainClass = new MainClass();
I can do for example
$MainClass->utils->show_text("Hello World");
And works fine.
What I'd like to do is... within the $MainClass->utils->test() (Another test method), is to be able to access $MainClass->db without using global or $GLOBALS.
Is there any alternative way of achieving this? To be able to access $MainClass methods and submethods within another submethod (access db from utils and from the main page where MainClass is initialized)? How would it be? I want to be able to access al the submethods, like utils being able to access db and forms method. as well as the pages that are outside MainClass.
Thank you
If utils has to use db, you either have to pass the MainClass instance to utils, so it can call $this->myRefToMain->db, or pass the instance of db itself, so utils can call $this->db. Either way, it cannot (reasonably) crawl up the call stack to find the object that called it.
Object if your class class_utils can exists without MainClass. And its method test() should access some object db of class class_db. This means class_utils depends on class_db and you should inject object of class_db in constructor, for example:
class MainClass {
public $db;
public $forms;
public $pagination;
function __construct() {
$this->db = new class_db();
$this->forms = new class_forms();
$this->utils = new class_utils($this->db);
}
}

Global Variables vs. Class Recursion and Overhead

I'm building an app based on an existing MVC framework.
The native app bootstraps up by creating a registry object, then populates it with all the classes needed, which is great.
$registry = new \App\Registry;
$loader = new \App\Loader;
$registry->set('load', $loader);
But when a descendant class needs access to the registry or another instantiated object, they're passing in the registry object and setting it as variable in the object of the new class.
$theme = new \App\Theme($registry);
$registry->set('theme', $theme);
Then inside the theme class:
class Theme {
private $registry;
public function __construct($registry) {
$this->registry = $registry;
}
}
This is creating recursion on each instance and by the time the app gets to the front controller class, it's this huge bloated object with massive recursion that's completely unnecessary.
I know that using global variables is frowned upon, and I don't use them unless absolutely needed, but is it not much more efficient to simply call the registry object as a global only where it's needed so as to not have all this bloat?
My solution is to just call the registry as a global within the method in a given class where I need to access an object that's already been registered.
For instance in my example, let's say I need to access the loader object from within a method in Theme.
class Theme {
public function __construct() {
$this->setName();
}
public function setName() {
global $registry;
$loader = $registry->get('load');
$loader->setName();
}
}
This removes the need to set the registry as a variable inside theme, so that each object is only created once.
Is there anything wrong with this approach? Are there security issues? Is there another technique that would produce the result I want without using a global reference?

Declare a variable created inside of a class as a global variable

How do I make a variable that is created within a class function available outside of that class function?
For instance, the constructor of my main class creates an instance of the logging class. I want this $log variable to be available in other classes (using the global keyword) I would assume.
Is this possible?
You can use static.
class Main
{
static private $log;
public function __construct()
{
self::$log = new Log();
}
static public function getLog()
{
return self::$log;
}
}
Now you can get access to $log everywhere using
Main::getLog();
Using a global would defeat the purpose of OOP. OOP is meant to alleviate dependence and provide code containment for modular use.
You are simply approaching your application design wrong, instead, design ease of access to the logging instance. That is; instantiate your logging class and store the instance somewhere that's accessible (by design) by other classes that depend on it. A sort of "registry".

$this->class->function(): how does it work?

I've been programming in PHP for several years and I've only just recently begun to look at object oriented code. Now I understand classes and such:
class Myclass {
public function __construct() {
}
}
and all that good stuff... I also understand creating functions and calling in my index.php:
$someVar = new Myclass;
One thing I've been trying to understand, being that i've recently looked at codeigniter and I like one thing about it and want to try and accomplish the same thing without actually using codeigniter.
in code igniter they have the variable $this appear to be their class variable. But by using that, you're able to call from multiple classes all at once.. such as:
$this->load->module(); which is in one class file..
$this->db->query(); which is in another class file.
I've searched google for the last few days trying to figure out how to do this same thing where each class would have the correlation between them all allowing me to run $this->class_name->function_name in my projects instead of creating a new variable for each class or for the sake of a clean index file, having every function in a single class file.
Any information (aside from buy this book - as that isn't an option for me) is greatly appreciated and I will thank you now (and will probably thank you again later just for good measure).
I've been reading you and Phil's comments. First off, you can't use $this on index.php. $this can only be used in the context of an object. So you could do,
$someVar = new Myclass;
...
$someVar->db->something();
...instead.
I'm not entirely sure what you mean by "read classes," but you can assign members to MyClass exactly as Phil indicates:
class MyClass {
private $inj;
function __construct(Injected $inj) {
$this->injected = $inj;
}
}
Note that the private $inj declaration is not mandatory, but skipping it is dangerous. Any non-declared members added are automatically public, and this can potentially screw with you if you rely on magical get. I would declare the properties you need.
Finally, you either need to include all class definitions you will use, use a function like load_class(), or use autoloading. If Injected above is not a declared class, you will get an error when trying to create one. load_class() almost certainly includes the class definition somehow.
The load and db references are class properties which are themselves other objects with public module() and query() methods respectively. For example
class MyClass
{
/**
* #var CI_DB
*/
private $db;
/**
* #var Loader
*/
private $load;
public function __construct(CI_DB $db, Loader $load)
{
$this->db = $db;
$this->load = $load;
// PHP also allows you to add arbitrary object properties
// that receive a "public" visibility
// Please note, this is not a good practice
$this->foo = new Foo;
}
public function someMethod()
{
$this->load->module();
$this->db->query();
// You can also use foo even though it is not defined
// in the class properties
$this->foo->something();
}
}
See http://www.php.net/manual/en/language.oop5.properties.php
Adding the foo property like we did is dangerous. Because it receives the public visibility, it may be altered externally to the class.
$class = new MyClass($db, $load);
$class->foo = new Bar;
$class->someMethod(); // will break if Bar does not contain a something() method

Can you explain how Zend's Action controller operating?

Sorry for constantly re-editing my question but looks like this is the only way to ask it properly.
My original problem is the following pseudo-code (a controller and it's parent) isn't working as i would like to:
class Parent {
var $data = array();
public function __construct( OtherClass $otherClass ) {
$this->data = $otherClass->getData(); //contains some => thing
$this->init($otherClass->getClassName());
}
public function init( $className ) {
new $className; //new Child
}
public function __get( $name ) {
return array_key_exists($name, $this->data) ? $this->data[$name] : null;
}
}
class Child extends Parent {
public function __construct() {
echo $this->some; //won't return 'thing';
}
}
fireeyedboy helped me a lot (thank you) and pointed out Zend_Controller_Action is doing what i want but i can't understand how they do it?
Original question
I know there was some similar questions here but i cannot dump them. Also i know i can reverse the whole process so i can initialize Child first then call parent::__construct but this seems unwanted for me. How can i access Parent variables easily in my case?
Update:
Let me clarify a little bit. Child is an arbitrary controller. Parent is the mother of all controllers. Many frameworks are doing the same but controllers can utilize their parent controllers variables, methods or objects without calling parent::__construct (and therefore filling child class constructors with unnecessary arguments). I don't like to rewrite any of these frameworks but i'd like to understand how they're operating.
Your child class doesn't call parent constructor. Here's a fix:
class Child extends Parent {
public function __construct() {
parent::__construct();
echo $this->some;
}
}
Update: Parent classes' constructors aren't called automatically in PHP. See the documentation:
Note: Parent constructors are not
called implicitly if the child class
defines a constructor. In order to run
a parent constructor, a call to
parent::__construct() within the child
constructor is required.
So what you're asking does not happen. Either you have misinterpreted the class structure or how they operate. Note that PHP also supports legacy constructor naming (at least until 5.3.3): If there is no __construct() method in a class, PHP assumes the constructor is named after the class (ie. class Foo { function Foo() {}) treats the Foo() method as constructor).
Injecting ANYTHING using controller's construct is generally a BAD PRACTICE! Problem lies in your design. What kind of object is OtherClass? Is it DB adapter? Is it ACL? Is it some helper class?
If you need external class in your controller I suggest using action helpers. That's what they are created for ;) Or create an action helper that will fetch this OtherClass from somewhere when needed.
You should NEVER use __construct() to do any of your dirty work. That's what init() is used for. But it has no params. And there is a reason for that - again - you should not inject dependencies like this ;)
Update:
Note your class uses discouraged PHP4 member variable definition syntax. Try replacing your var with protected.
The problem is that your Child class does not call the base constructor.
Incidentally, Parent is not a valid class name in PHP. I have changed the class names for clarity. See below:
class ChildClass extends ParentClass {
public function __construct() {
parent::__construct(/* what goes here? */);
echo $this->some;
}
}
However, note the what goes here? part: your base class requires a reference to an OtherClass instance to be constructed. Therefore, since ChildClass IS-A ParentClass, it also needs to get such an instance somehow. You will need to either add a parameter to ChildClass::__construct and forward the value to parent::__construct, or somehow figure out a default value yourself.

Categories