Is there anything wrong with this practice example?
class Test {
static $_instance = null; // self
protected $_name = null;
/**
* Get instance
* #return [type] [description]
*/
public static function getInstance() {
if(is_null(self::$_instance))
self::$_instance = new self;
return self::$_instance;
}
/**
* Set name
*/
public static function setName($name) {
self::getInstance()->_name = $name;
}
public static function getName() {
return self::getInstance()->_name;
}
}
And then doing this anywhere:
Test::setName('Some name');
Test::getName(); // returns "Some name"
Instead of:
// somewhere early
$test = new Test;
// somewhere else
function someRandomFunction() {
global $test;
$test->setName('Hello there');
}
// somewhere else
function AnotherRandomFunction() {
global $test;
return $test->getName();
}
I wrote a test that looks like this:
class TestTest extends WP_UnitTestCase {
function testInstance() {
$someName = 'Hello ' . time();
Test::setName($someName);
$this->assertEquals(Test::getName(), $someName);
}
}
This being my very first test I write, I am unsure whether my test and approach to this is considered good practice.
Related
so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.
I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.
I am trying to pass a class as a parameter but I do not know if it is possible.
class User {
var $name;
}
class UserRepository {
private $type;
public function __construct(Class) {
$this->type = Class;
}
public function getInstance() {
return new $this->type;
}
}
$obj = new UserRepository(User);
I am accepting suggestions on other ways to do it as well.
Just instantiate the class and call that
$user = new User();
$obj = new UserRepository($user);
Another option (since User contains only variables) is to make the variable static and use that
class User {
public static $name;
}
$obj = new UserRepository(User::$name);
I think you are just looking for a string:
class User {
var $name;
}
class UserRepository {
private $type;
public function __construct($Class) {
^^^^^^ this will be a string
$this->type = $Class;
}
public function getInstance() {
return new $this->type;
}
}
$obj = new UserRepository('User');
^^^^^^ send a string here
var_dump($obj->getInstance());
output:
object(User)#2 (1) { ["name"]=> NULL }
An example.
Singleton Class:
<?php
class db_singleton
{
const ORACLE_HOST = "SOMEIP";
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance; // stores the oci_* instance
private function __construct() { } // block directly instantiating
private function __clone() { } // block cloning of the object
public static function call()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public function __destruct() {
oci_close($instance);
}
public function queryresult($query)
{
$result_set_array =array();
$this->stmt = oci_parse($this->con, $query);
oci_execute($this->stmt);
while($row=oci_fetch_array($this->stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($this->stmt);
return $result_set_array;
}
}
?>
When I try using singleton class with below code, it works perfect and fetch results.
$conn = db_singleton::call();
$stid = oci_parse($conn, 'SELECT * FROM somevalid_table');
oci_execute($stid);
while($result=oci_fetch_array($stid,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $result;
}
Now, when I try extending my class using model, it throws exception
class Myclass Extends db_singleton{
public function someModel()
{
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Exception:
Fatal error: Call to private db_singleton::__construct() from context 'someController'
I know that class cannot be instantiated having private constructor. __construct() functions are always called when an object is instantiated, so trying to do something like $x = new MyObject() will cause a fatal error with a private construction function.
I am using Singleton classes to prevent direct instantiation of an object. How can I overcome issue ? What would be the best solution ?
Thanks.
$x = new MyObject() will never work if your constructor is private in that class because __construct() is the first method that is invoked on object creation.
Create a public method
/**
* Singleton class
*
*/
final class UserFactory
{
/**
* Call this method to get singleton
*
* #return UserFactory
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instance it
*
*/
private function __construct()
{
}
}
To use:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
But:
$fact = new UserFactory()
PHP Class Script:
class db_singleton
{
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance = null; // stores the oci_* instance
// private constructor
private function __construct() { } // block directly instantiating
private function __clone() { trigger_error('Clone is not allowed.', E_USER_ERROR); } // block cloning of the object
public static function getInstance()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public static function queryresult($query)
{
$result_set_array =array();
$stmt = oci_parse(db_singleton::getInstance(), $query);
oci_execute($stmt);
while($row=oci_fetch_array($stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($stmt);
return $result_set_array;
}
Now to prevent Fatal error: Call to private db_singleton::__construct(), I have added an empty constructor to my child class which is model class in my case. This will override the parent class constructor which is private.
class Myclass Extends db_singleton{
public function __construct() {}
public function someModel(){
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Hope it helps someone.
Thanks.
Bellow is a PHP script.
I tried to implement the Observer pattern (without MVC structure)... only basic.
The error which is encountered has been specified in a comment.
First I tried to add User objects to the UsersLibrary repository. There was a error such as User::update() does not exists or something.
Why is that error encountered? What fix should be applied and how?
interface IObserver {
public function update(IObservable $sender);
}
interface IObservable {
public function addObserver(IObserver $obj);
public function notify();
}
class UsersLibrary implements IObservable {
private $container;
private $contor;
//private $z;
public function __construct() {//IObserver $a) {
$this->container = array();
$this->contor = 0;
echo "<div>[constructing UsersLibrary...]</div>";
$this->addObserver(new Logger());
//$this->z = $a;
}
public function add($obj) {
echo "<div>[adding a new user...]</div>";
$this->container[$this->contor] = $obj;
$this->contor++;
$this->notify();
}
public function get($index) {
return $this->container[$index];
}
public function addObserver(IObserver $obj) {
$this->container[] = $obj;
}
public function notify() {
echo "<div>[notification in progress...]</div>";
foreach($this->container as $temp) {
//echo $temp;
#################################################################
$temp->update(); //--------ERROR
//Fatal Error: Call to a member function update() on a non-object.
#################################################################
}
//$this->container[0]->update();
//$this->z->update($this);
}
}
class User {
private $id;
private $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
}
class Logger implements IObserver {
public function __construct() {
echo "<div>[constructing Logger...]</div>";
}
public function update(IObservable $sender) {
echo "<div>A new user has been added.</div>";
}
}
$a = new UsersLibrary(); //new Logger());
//$a->add(new User(1, "DemoUser1"));
//$a->add(new User(2, "DemoUser2"));
$a->add("Demo");
echo $a->get(0);
//echo $a->get(0)->getName();
Your User class is not implementing interface IObserver and therefore is not forced to have the method update().
You have to instantiate a new User() in order to add it to the UsersLibrary:
$library = new UsersLibrary();
$user = new User(1, "Demo");
$library->add($user);
Also, you are mixing Users and Loggers into your UsersLibrary container. Maybe think about separating the containers for them?
You are passing a string instead of an object in your $a->add() call. You should either pass in an object, or alter the code in UserLibrary::add() to wrap it's argument in an appropriate object (or do an object lookup of it sees a string, for instance find a user with that name).
$user = new User(1, "Demo");
$a = new UsersLibrary();
$a->add($user);
I would like to have a base class with basic properties and functions, so I dont have to define them in all child classes.
I use php 5.3.3.
Is this impossible ?
class A {
private $debug;
private $var;
protected function setVar($str) {
$this->debug = 'Set by function `'. MAGIC_HERE .'` in class `'. get_called_class() .'`.';
$this->var = $str;
return true;
}
protected function getVar() {
return $this->var;
}
protected function getDebug() {
return $this->debug;
}
}
class B extends A {
public function __construct() {
$this->doSomething();
}
public function doSomething() {
$this->setVar('my string');
}
}
$myobj = new B();
$myobj->getDebug();
// expected output "Set by function `doSomething` in class `B`."
<?php
class A {
private $debug;
private $var;
protected function setVar($str) {
$this->debug = 'Set by function `'. MAGIC_HERE .'` in class `'. get_called_class() .'`.';
$this->var = $str;
return true;
}
protected function getVar() {
return $this->var;
}
// Notice the public here, instead of protected //
public function getDebug() {
return $this->debug;
}
}
class B extends A {
public function __construct() {
$this->doSomething();
}
public function doSomething() {
$this->setVar('my string');
}
}
$myobj = new B();
echo $myobj->getDebug();
// expected output "Set by function `doSomething` in class `B`."
You had just two small issues. A::getDebug needed to be public to be accessible from the outside and you forgot to output the return of A::getDebug.
See the debug_backtrace function. Note this function is expensive, so you should disable those debug features in production.
Is this no good for you?
I'm not running 5.3 locally, so I had to switch out get_called_class() but you could still use it. Should have made that clear, sorry.
class A {
private $debug;
private $var;
protected function setVar($str, $class) {
$this->debug = 'Set by function `` in class `'. $class .'`.';
$this->var = $str;
return true;
}
protected function getVar() {
return $this->var;
}
public function getDebug() {
return $this->debug;
}
}
class B extends A {
public function __construct() {
$this->doSomething();
}
public function doSomething() {
$this->setVar('my string', __CLASS__);
}
}
$myobj = new B();
echo $myobj->getDebug();