__callStatic not being called - php

Not sure why but Its not even hitting the var_dump() that I have. Lets look at how I have it implemented.
<?php
namespace ImageUploader\Controllers;
class ApplicationController implements \Lib\Controller\BaseController {
....
public function beforeAction($actionName = null, $actionArgs = null){}
public function afterAction($actionName = null, $actionArgs = null){}
public static function __callStatic($name, $args) {
var_dump('hello?'); exit;
if (method_exists($this, $name)) {
$this->beforeAction($name, $args);
$action = call_user_func(array($this, $name), $args);
$this->afterAction($name, $args);
return $action;
}
}
}
As we can see I want to do something before and after an action is called, regardless if you implemented the method or not. But that var_dump is never reached.
This class is extended in:
<?php
namespace ImageUploader\Controllers;
use \Freya\Factory\Pattern;
class DashboardController extends ApplicationController {
public function beforeAction($actionName = null, $actionArgs = null) {
var_dump($actionName, $actionArgs); exit;
}
public static function indexAction($params = null) {
Pattern::create('\Freya\Templates\Builder')->renderView(
'dash/home',
array(
'flash' => new \Freya\Flash\Flash(),
'template' => Pattern::create('\Freya\Templates\Builder')
)
);
}
....
}
Now when I do: DashboardController::indexAction(); it should exit ... unless I am missing something. If that's the case - what is it?
even the var_dump in the before_action(...) that's implemented is never reached (obvi' because of the first one, but if I take out the first one the second is never reach.)

__callStatic is called only when a static method does not exist - as indexAction actually is defined, it is executed without bothering __callStatic(). (Documentation)
An approach to achieve what you are trying to do could be by wrapping your controller inside a decorator:
class ExtendedApplicationController
{
/**
* #var \Lib\Controller\BaseController
*/
protected $controller;
function __construct(\Lib\Controller\BaseController $controller) {
$this->controller = $controller;
}
function __callStatic($name, $args) {
if (method_exists($this->controller, 'beforeAction')) {
call_user_func_array(array($this->controller, 'beforeAction'), $name, $args);
}
call_user_func_array(array($this->controller, $name), $args);
if (method_exists($this->controller, 'afterAction')) {
call_user_func_array(array($this->controller, 'afterAction'), $name, $args);
}
}
}
and then, in your code, you could do:
$controller = new ExtendedApplicationController(new DashboardController());
$controller::indexAction();
I have to warn you that I didn't test this approach while I was writing it, but I hope it gives you an idea!

Related

how to call method with "::" in the first and "->" for the rest in method chaining

I want to make my own class that I can use for interacting with a database,
and I think it would be easy and readable if I use method chaining.
But i have a problem calling the first method statically.
Here is the code:
<?php
class Crud
{
protected static $action;
protected static $instance = null;
protected static $columns = [];
protected $data;
protected $db;
protected $query;
protected $table;
public function __construct()
{
$this->db = new mysqli('localhost', 'root', '', 'bahan_belajar');
if (!$this->db) {
echo "error";
}
return $this;
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
public function select()
{
if (empty(func_get_args())) {
// $this->columns = "*";
self::$columns = "*";
} else {
if (is_array(func_get_args())) {
// self::columns = join(', ', func_get_args());
self::$columns = join(', ', func_get_args());
} else {
// self::columns = func_get_args();
self::$columns = func_get_args();
}
}
self::$action = "SELECT";
return $this;
}
public function from($tableName)
{
$this->table = ' FROM ' . $tableName;
return $this;
}
public function get($getName = 'object')
{
$this->query = self::$action . ' ' . self::$columns . ' ' . $this->table;
switch ($getName) {
case 'object':
$this->data = $this->db->query($this->query)->fetch_object();
break;
case 'array':
$this->data = $this->db->query($this->query)->fetch_array();
break;
case 'count':
$this->data = $this->db->query($this->query)->num_rows;
break;
}
return $this->data;
}
}
$chat = Crud::getInstance()->select('nama', 'teks')->from('chat')->get();
echo '<pre>';
print_r($chat);
echo '</pre>';
Actually, this code works fine if I use getInstance() method at first as shown above. But how can I make it work when I call directly to the select() method as a static method like:
$chat = Crud::select('nama', 'teks')->from('chat')->get();
If I run the code above I will get an error such as:
Fatal error: Uncaught Error: Using $this when not in object context in C:\xampp\htdocs\bahan_belajar\chat\classes.php:47 Stack trace: #0 C:\xampp\htdocs\bahan_belajar\chat\classes.php(74): Crud::select('nama', 'teks') #1 {main} thrown in C:\xampp\htdocs\bahan_belajar\chat\classes.php on line 47
I know select() method should be a static method before it can be called with :: (I think), but how can I make it be static?
You can only use :: with static methods and -> with instance methods. This leaves you with 2 options:
Option 1: Get an Instance Explicitly Before Chaining
You need to get an instance with your static method, then use -> to chain, like this:
Crud::getInstance()->select('nama', 'teks')->from('chat')->get();
Option 2: Get Fancy with Magic Methods
Your other option is to get fancy with magic methods. If you make your methods protected or private, you can intercept calls to those methods from outside the class with the __call() and __callStatic() magic methods. When you're in __callStatic(), you can switch over to using an instance by invoking self::getInstance().
This approach would let you do something like
Crud::select('nama', 'teks')->from('chat')->get();
Here's some very simplified sample code to demonstrate the idea (demo on 3v4l):
class Test
{
public static $instance;
protected $myVar = 'foo';
// This intercepts instance calls ($testObj->whatever()) and handles them
public function __call($name, $args)
{
return call_user_func_array(array($this, $name), $args);
}
// This intercepts instance calls ($testObj->whatever()) and handles them
// The use of self::getInstance() lets us force static methods to act like instance methods
public static function __callStatic($name, $args)
{
return call_user_func_array(array(self::getInstance(), $name), $args);
}
public static function getInstance()
{
return self::$instance ? : new self;
}
protected function getMyVar()
{
echo $this->myVar;
}
protected function setMyVar($value)
{
$this->myVar = $value;
return $this;
}
}
echo Test::setMyVar(15)->getMyVar(); // successfully echoes 15
An important note to all of this: what you are doing looks a lot like reinventing Eloquent, the ORM that ships with Laravel. Take it from somebody who has built his own ORM before: you're better off using an existing system like Eloquent, which has already been written and thoroughly tested (and can be used without requiring you to use the entire Laravel framework). Building an ORM is much harder than it looks, and, once you start, the rabbit hole just keeps getting deeper.

My mixin class is causing my classes to become static - PHP

So this is my mixin class:
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$object_name = get_class($object);
$this->_class_objects[$object->name] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object_name => $class_object){
$this->_methods[$class_object_name] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $class_name=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->isParam($class_name, $method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function isParam($class_name, $method, $param){
if($param != null){
call_user_func(array($class_name, $method), $param);
}else{
call_user_func(array($class_name, $method));
}
}
}
Pretty simple stuff, load a set of classes, allow you to call their functions and so on, but we have a new issue. It seems that classes passed into this are instantiated as static, thus their methods cannot use $this-> they are resorted to using self:: which is wrong.
Lets see an example of how this all works:
class BaseBridge extends AisisCore_Loader_Mixins{
public function __construct(){
parent::construct();
$this->setup(array('ClassB' => array()));
}
}
Lets Define ClassB
class ClassB{
public function __construct(){}
public function some_method(){
$this->_some_private_method();
}
private function _some_private_method(){}
}
Pretty basic stuff, so lets hook it all up in ClassA
class ClassA extends BaseBridge{
public function __construct(){
parent::__construct();
$this->some_method();
}
}
Quick Review: We have a core class, ClassA which extends BaseBridge which is our bridge class between one or more (meant to be used with more) classes that ClassA extends from. In this case were only extending from ClassB for simplicity.
Whats the issue? See, how in ClassB, were doing: $this->_some_private_method(); ya that's going to epically and catastrophically fail. Why? because I get the error: Using $this when not in object context which makes me so confused, so I change it to: self::$_some_private_method(); and it works like a charm.
Why? and what do I have to change or fix to make it so that $this can be used in a class being instantiated through the mixin class?
So with some slight modifications, I have managed to make this work. How ever I do not believe that a function with multiple arguments will work - Feed back appreciated.
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$this->_class_objects[$object->name] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object_name => $class_object){
$this->_methods[$class_object_name] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $class_name=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->_is_param($class_name, $method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function _is_param($class_name, $method, $param){
if($param != null){
$this->_param_is_array($class_name, $method, $param);
}else{
call_user_func(array($this->_class_objects[$class_name], $method));
}
}
private function _param_is_array($class_name, $method, $param){
if(is_array($param)){
call_user_func_array(array($this->_class_objects[$class_name], $method), $param);
}else{
call_user_func(array($this->_class_objects[$class_name], $method, $param));
}
}
}
Now functions inside of classes that are registered by this class can use $this->.
The issue is that I am not sure if multiple param based functions will actually work.

Creating a static array without changing thousands of lines of code

We have a class that holds a public array called $saved that contains lots of data required to share between methods (example below)...
class Common {
public $saved = array();
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUserID() {
return $this->saved['user_data']['id'];
}
}
There are literally thousands of lines of code that work like this.
The problem is that new instance of classes that extend Common are being made within some methods so when they access $saved it does not hold the same data.
The solution is to make $saved a static variable, however I can't change all of the references to $this->saved so I want to try and keep the code identical but make it act static.
Here is my attempt to make $this->saved calls static...
class PropertyTest {
private $data = array();
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
class Common {
public $saved;
private static $_instance;
public function __construct() {
$this->saved = self::getInstance();
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new PropertyTest();
self::$_instance->foo = array();
}
return self::$_instance->foo;
}
}
This doesn't quite work when setting a variable it doesn't seem to stay static (test case below)...
class Template extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data'] = array('name' => 'bob');
$user = new User();
}
}
class User extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data']['name'] .= " rocks!";
$this->saved['user_data']['id'] = array(400, 10, 20);
}
}
$tpl = new Template();
print_r($tpl->saved['user_data']);
$this->saved is empty when User gets initialized and doesn't seem to be the same variable, the final print_r only shows an array of name => bob.
Any ideas?
First of all, I have to say that, IMO, it is not that good to use an instance's property as a class's property ($saved is not declared as static but its value is shared with all instance).
Here is a working version http://codepad.org/8hj1MOCT, and here is the commented code. Basically, the trick is located in using both ArrayAccess interface and the singleton pattern.
class Accumulator implements ArrayAccess {
private $container = array();
private static $instance = null;
private function __construct() {
}
public function getInstance() {
if( self::$instance === null ) {
self::$instance = new self();
}
return self::$instance;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
class Common {
public $saved = null;
public function __construct() {
// initialize the "saved" object's property with the singleton
// that variable can be used with the array syntax thanks to the ArrayAccess interface
// so you won't have to modify your actual code
// but also, since it's an object, this local "$this->saved" is a reference to the singleton object
// so any change made to "$this->saved" is in reality made into the Accumulator::$instance variable
$this->saved = Accumulator::getInstance();
}
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUser() {
return $this->saved['user_data'];
}
}
class Template extends Common {
// you can redeclare the variable or not. Since the property is inherited, IMO you should not redeclare it, but it works in both cases
// public $saved = null;
public function __construct() {
// maybe we can move this initialization in a method in the parent class and call that method here
$this->saved = Accumulator::getInstance();
}
}
I think there are a number of issues with this implementation that could well come back to bite you. However, in your current implementation your contructing a new instance (albeit through a static call) every time.
Instead use getInstance() as your singleton hook, and make your __construct private, as you'll only be accessing it from with the context of the Common class.
Like so:
class Common {
public $saved;
private static $_instance;
private function __construct() {
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
... any other modifications you want to make ....
}
return self::$_instance;
}
}
And don't ever run parent::_construct(), instead always use the getInstance() method.
You might also want to ditch the idea of extending this singleton class. This is really a bad antipattern and could cost you a number of issues in the long run. Instead just maintain a Common class that other classes can read / write to. As its a singleton you don't need to worry about injection.
I seem to have solved the problem, by making $this->saved a reference to a static variable it works...
class Common {
private static $savedData = array();
public $saved;
public function __construct() {
$this->saved =& self::$savedData;
}
}

Class function only available to "parent" - PHP

I want to create a function in a class that is available for a set of users, but that they won't be able to access. Ex:
class Stuff_for_user {
private $errors;
/*
* private $errors gets modified by private functions
*/
public function get_errors(){ // This is for users to display errors.
return $this->errors;
}
/*something here...*/ function set_errors($str){
$this->errors = $str;
}
}
So far so good, but now I want the parent class to be able to set Stuff_for_User's errors:
class Main_mess {
public index(){
$user_available_data = new Stuff_for_user();
if($big_error)
$user_available_data->set_errors("BIG ERROR!!!");
$this->send_to_users($user_available_data);
}
}
I want only Main_mess to be able to access Stuff_for_User's set_errors() method. Is that possible?
No, that is not possible like that, since Main_mess is not a parent class of Stuff_for_users (and this is probably what you want, looking at what your code actually does). So set_errors has to be public if you want to call it from the outside.
This is not possible how you want to implement it.
Some ideas (i dont know why or how you want to do that but just ideas...):
do set_error($str,$access_key) and let $access_key be an access string only you know!
let Stuff_for_user be in Extended_Stuff_for_user which has the set_error function like:
class Extended_Stuff_for_user {
private $errors;
private $Stuff_for_user;
public function set_errors() {
/* ... */
}
public function getStuffForUser() {
return $this->Stuff_for_user;
}
}
It seems that you are looking for implementation of something called friend class in php. Well .. i'm sorry to tell you this, but it is not possible.
You should look at other possible solutions to your problem.
class SecureContainer{
protected $user = null;
protected $target = null;
public function __construct( $target, $user )
{
$this->target = $target;
$this->user = $user;
}
public function __call( $method, $arguments )
{
if ( $this->user->isAllowed(getType( $this->target ), $method))
{
return call_user_func_array(
array( $this->target, $method), $arguments );
}
}
}
Use it like this:
$something = new UnsecureSomething;
$user = new User( $uid );
$something = new SecureContainer( $something, $user );
This should let you control the access to methods.
Yes it possible but it can be dirty.
Like This.
class Stuff_for_user {
private $errors;
/*
* private $errors gets modified by private functions
*/
public function get_errors(){ // This is for users to display errors.
return $this->errors;
}
/*
This way the child classes of Main will able be to use the set_errors function;
*/
function set_errors($class,$str){
if($class instanceof Main_mess)
{
$this->errors = $str;
}
/*
AndThis way the only Main_mess will be able;
*/
function set_errors($class,$str){
if(get_class($class)=="Main_mess")
{
$this->errors = $str;
}
}
class Main_mess {
public index(){
$user_available_data = new Stuff_for_user();
if($big_error)
$user_available_data->set_errors($this,"BIG ERROR!!!");
$this->send_to_users($user_available_data);
}
}

Checking if an overridden parent method exists before calling it

How would I go about ensuring that the overridden parent method exists before I call it?
I've tried this:
public function func() {
if (function_exists('parent::func')) {
return parent::func();
}
}
However the function_exists never evaluates to true.
public function func()
{
if (is_callable('parent::func')) {
parent::func();
}
}
I use this for calling parent constructor if exists, works fine.
I also use the following as a generic version:
public static function callParentMethod(
$object,
$class,
$methodName,
array $args = []
) {
$parentClass = get_parent_class($class);
while ($parentClass) {
if (method_exists($parentClass, $methodName)) {
$parentMethod = new \ReflectionMethod($parentClass, $methodName);
return $parentMethod->invokeArgs($object, $args);
}
$parentClass = get_parent_class($parentClass);
}
}
use it like this:
callParentMethod($this, __CLASS__, __FUNCTION__, func_get_args());
The way to do that, is:
if (method_exists(get_parent_class($this), 'func')) {
// method exist
} else {
// doesn't
}
http://php.net/manual/en/function.method-exists.php
http://php.net/manual/en/function.get-parent-class.php
<?php
class super {
public function m() {}
}
class sub extends super {
public function m() {
$rc = new ReflectionClass(__CLASS__);
$namepc = $rc->getParentClass()->name;
return method_exists($namepc, __FUNCTION__);
}
}
$s = new sub;
var_dump($s->m());
gives bool(true). Not sure if this would work if the method was defined in a superclass of super, but it would be a matter of introducing a simple loop.

Categories