I am trying to make a script in which different classes (e.g. Database, Utilities, Config) are all used to form one central Main class. I have tried extending a chain of them:
Main -> Utilities -> Database -> Configuration
But how can I set the different parts so that they can be called like this:
<?php
$this->db->select("WAFFLES");
echo($this->config->app_path);
?>
You could create a global class that does you basic initializing
class Base {
$var1, var2;
public function __construct() {
$this->var1 = new DB();
$this->var2 = new Config();
....
}
}
Then your classes can extend the base class and have access to the data
class Foo extends Base {
public function bar() {
$this->var1->someOpertaion();
}
}
You need to declare each new object as variable in your Main Class like:
class Main{
private $db = NULL;
private $config = NULL;
$this->db = new Database;
$this->config = new Config;
}
etc.
While i'm not a professional coder i'll considering a better approach than this. This kind of object-handling can cause a bloated main class and in the worst case you may face some performance issues.
1) use __autoload or spl_autoload_register to load classes
2) use magic methods, to call function when getting unknown property. Following examples demonstrates how to use __get and dynamicaly initialize object only when you use them.
//use __autoload to load db and config class when they are called.
class db{
function lol(){
echo 'Hello from db->lol() <br />';
}
}
class config{
function lol(){
echo 'Hello from config->lol() <br />';
}
}
//Manager class to use with classes where you want to access other object trough $this
class Manager{
private $_instances=array();
function __get($name){
//if instance does not exists, create one
if (!isset($this->_instances[$name])){
$this->_instances[$name]=new $name();
}
//return instance
return $this->_instances[$name];
}
}
class Some extends Manager{
function f1(){
$this->db->lol();
$this->config->lol();
}
}
$some=new Some();
$some->f1(); //echoes 'Hello from db->lol()' and 'Hello from config->lol()'
But for accessing global class instances I prefer using following method:
Use singleton pattern to access global class trough GloballClass::i() and if global class is not defined use autoload to load that class.
class db extends mysqli{
private static $_i;
//Access to singleton instance
public static function i() {
return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
}
//class functions
function q($q){
echo 'Hello from db->q()';
}
}
class config{
private static $_i;
//Access to singleton instance
public static function i() {
return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
}
//class functions
function somefunction(){
echo 'Hello from config->somefunction()';
}
}
db::i()->q('SELECT * FROM users');
config::i()->somefunction();
Following is solution inspired by Gordons comment:
It uses GlobalClassFactory class to define only one instance of global classes.
class db{
function lol(){
echo 'Hello from db->lol() <br />';
}
}
class config{
function lol(){
echo 'Hello from config->lol() <br />';
}
}
class GlobalClassFactory{
private static $_classes=array();
public static function getInstance($name){
if (!isset(self::$_classes[$name])){
self::$_classes[$name]=new $name();
}
return self::$_classes[$name];
}
}
class Base{
function __get($name){
return GlobalClassFactory::getInstance($name);
}
}
class Some extends Base{
function f1(){
$this->db->lol();
$this->config->lol();
}
}
$some=new Some();
$some->f1();
Here is the sample prototype:
include 'db.php'; // include db class
include 'config.php'; // include config class
class main{
public $db = NULL;
public $config = NULL;
function __construct() {
$this->db = new db;
$this->config = new config;
}
}
Creating a composite object with instances of everything that might be needed during code execution up front is a complete waste of resources. You want to create instances only when needed. One way to achieve this would be to add a magic __get method to the class:
public function __get($name) {
// if self::$instances (or main) contains instance of $name, return instance
// else if class_exists $name, create, store and return instance
// else throw exception
}
But even then, chances are you are creating a God Object and magic methods are somewhat slower than regular accessors. If you need to create instances this way, have a look at the Symfony Dependency Injection Container or implement a Registry.
Related
I'm learning OOP PHP. I want to call a method from another class to a new class.
For just a example:
<?php
class Aclass {
function aMethod($input)
{
echo 'Hello a world ';
}
}
?>
And i want to call the method aMethod from the class 'Aclass' into the new class.
<?php
class Bclass {
//calling the method here?
}
?>
i tried extending , still not working for me.
Thanks.
In your class Bclass you should create some functions. In case below you are creating a new instance of Aclass and then using function aMethod.
Example
<?php
class Bclass {
public function __construct() {
$a = new Aclass();
$a->aMethod("some_text");
}
}
?>
Other way is extend Bclass. In this case your class Bclass extends everything what's in Aclass so you can use it just with $this.
Example
<?php
class Bclass extends Aclass {
public function __construct() {
$this->aMethod("some_text");
}
}
?>
Also your function aMethod in Aclass should have public or protected visibility. Public if you create an instance, protected if you extends. More informations can be found in manuals at the end.
Example
<?php
class Aclass {
public function aMethod($input) // protected if you will extend this class
{
echo 'Hello a world ';
}
}
?>
You can of course use both methods not only in __construct but also in other functions.
Manuals
PHP: Visibility
PHP: Constructors and Destructors
For this I'd use dependency injection. Which is just a fancy way of saying "sending an object of the A class when creating B".
In other words, something like this:
class typeA {
public function __construct () {};
public function test () {
return 'Test string';
}
}
class typeB {
protected $testObj;
public function __construct (typeA $testCase) {
$this->testObj = $testCase;
}
public function getTest () {
return $this->testObj->test ();
}
}
$a = new typeA ();
$b = new typeB ($a);
echo $b->getTest ();
Constructors are meant to be used to create an object that's ready to be used, which is why I've just stored the dependency inside the typeB object itself. Then, in the getTest() method I invoke the test() method of the object I'm depending upon, in order to get the needed data from it.
Doing it in this manner will allow you to write flexible OOP code, which can easily be expanded and extended as you require. Hiding the dependencies inside the constructors, by creating objects there, creates a hidden and hard dependency. Something which makes it a lot harder, if not down right impossible, to properly leverage the extensible nature of the class-based designs.
I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"
I am learning on PHP classes. I can write a basic PHP classes but having trouble when accessing variables and functions from other classes.
My first class
Class First_class
{
public $options;
function __construct()
{
$this->options = get_option('theme_options'); //wordpress fn
}
function my_function() {
add_menu_page(...)
}
}
Class Second_class
{
public $first_class_object;
function __construct()
{
$this->first_class_object = new First_class();
//Trying to access $this->options here
}
}
new Second_class();
Class Third_class
{
public $first_class_object;
function __construct()
{
$this->first_class_object = new First_class();
//Trying to access $this->options here
}
}
new Third_class();
I am trying to access public variables from Class First_class using $first_class_object = new First_class(); in the second class and third classes. You see when I initiate new First Class in other classes add_menu_page() triggers two times. I am not sure how to access the variables and functions properly. May be constructors?
Sorry may be I am something wrong here?
I am looking for an experts suggestion.
Extend your classes.
An example is below using your code as an example. 3v4l demo is here and php extends docs are here.
<?php
class FirstClass
{
public $options;
public function __construct()
{
$this->options = ['yay']; //get_option('theme_options'); //wordpress fn
}
public function myFunction()
{
//
}
}
class SecondClass extends FirstClass
{
}
class ThirdClass extends FirstClass
{
}
$secondClass = new SecondClass();
var_dump($secondClass->options);
$thirdClass = new ThirdClass();
var_dump($thirdClass->options);
if you try accessing other class Properties, you could use className::propertyName
OR
if you wanna access properties within the class you can use self:: or $this.
know more about accessing properties and traverses under classes.
http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php
try extend class
class Third_class extends First_class
{
public $first_class_object;
function __construct()
{
$this->first_class_object = new First_class();
//Trying to access $this->options here
}
}
or use this construction for access to you class
$this->first_class_object->option
Still trying to figure out oop in PHP5. The question is, how to access a parent's static variable from an extended class' method. Example below.
<?php
error_reporting(E_ALL);
class config {
public static $base_url = 'http://example.moo';
}
class dostuff extends config {
public static function get_url(){
echo $base_url;
}
}
dostuff::get_url();
?>
I thought this would work from experience in other languages.
It's completely irrelevant that the property is declared in the parent, you access it the way you access any static property:
self::$base_url
or
static::$base_url // for late static binding
Yes, it's possible, but actually should be written like this:
class dostuff extends config {
public static function get_url(){
echo parent::$base_url;
}
}
But in this case you can access it both with self::$base_url and static::$base_url - as you don't redeclare this property in the extending class. Have you done it so, there would have been a distinction:
self::$base_url would always refer to the property in the same class that line's written,
static::$base_url to the property of the class the object belongs to (so called 'late static binding').
Consider this example:
class config {
public static $base_url = 'http://config.example.com';
public function get_self_url() {
return self::$base_url;
}
public function get_static_url() {
return static::$base_url;
}
}
class dostuff extends config {
public static $base_url = 'http://dostuff.example.com';
}
$a = new config();
echo $a->get_self_url(), PHP_EOL;
echo $a->get_static_url(), PHP_EOL; // both config.example.com
$b = new dostuff();
echo $b->get_self_url(), PHP_EOL; // config.example.com
echo $b->get_static_url(), PHP_EOL; // dostuff.example.com
I have a class with a factory-pattern function in it:
abstract class ParentObj {
public function __construct(){ ... }
public static function factory(){
//returns new instance
}
}
I need children to be able to call the factory function and return an instance of the calling class: $child = Child::factory(); and preferably without overriding the factory function in the child class.
I have tried multiple different ways of achieving this to no avail. I would prefer to stay away from solutions that use reflection, like __CLASS__.
(I am using PHP 5.2.5 if it matters)
If you can upgrade to PHP 5.3 (released 30-Jun-2009), check out late static binding, which could provide a solution:
abstract class ParentObj {
public function __construct(){}
public static function factory(){
//use php5.3 late static binding features:
$class=get_called_class();
return new $class;
}
}
class ChildObj extends ParentObj{
function foo(){
echo "Hello world\n";
}
}
$child=ChildObj::factory();
$child->foo();
In my humble opinion what you're trying to do makes no sense.
A factory pattern would work like this:
abstract class Human {}
class Child extends Human {}
class Fool extends Human {}
class HumanFactory
{
public static function get($token)
{
switch ($token)
{
case 'Kid' : return new Child();
case 'King': return new Fool();
default : throw new Exception('Not supported');
}
}
}
$child = HumanFactory::get('Kid');