PHP - Calling instance method from static method - php

I am having trouble calling a specific method from another class in my app. I have a class, Rest, that determines various settings, etc. about a particular request received by the server and creates a Rest object with the properties of the request. The Rest class may then call any given method in a separate class to fulfill the request. The problem is that the other class needs to call methods in the Rest class to send a response, etc.
How can this be possible? Here's a blueprint of my current setup:
class Rest {
public $controller = null;
public $method = null;
public $accept = null;
public function __construct() {
// Determine the type of request, etc. and set properties
$this->controller = "Users";
$this->method = "index";
$this->accept = "json";
// Load the requested controller
$obj = new $this->controller;
call_user_func(array($obj, $this->method));
}
public function send_response($response) {
if ( $this->accept == "json" ) {
echo json_encode($response);
}
}
}
The controller class:
class Users {
public static function index() {
// Do stuff
Rest::send_response($response_data);
}
}
This results in receiving a fatal error in the send_response method: Using $this when not in object context
What's the better way to do this without sacrificing the current workflow.

You can create a Rest instance in User:
public static function index() {
// Do stuff
$rest = new Rest;
$rest::send_response($response_data);
}
You could also change Rest to be a singleton and call an instance of it, but beware of this antipattern.

You need to create an instance first.
class Users {
public static function index() {
// Do stuff
$rest = new Rest();
$rest->send_response($response_data);
}
}

You don't call send_response() in an object context, as the error message says.
Either you create an instance and call everything on that instance (IMHO the right way), or you do everything statically, including the constructor (you may want to have a intialization method instead) and the properties.

Related

Make own loadhelper function (like in CodeIgniter)

I'm trying make every day something new. Now i want to make function to dynamic load helper without include and creating new class object
In code igniter that's look like
$this->load->helper('syslog_helper');
and now we can use
syslog_helper->some_function()
file is automatic including, object is automatic created and we can use them
Question is: how can I do the same think using pure PHP ?
Like this
Create a class name load that has a method named helper, if you want it accessible with $syslog_helper then load needs to be able to call the original class so when you create an instance pass the $this as part of its constructor. Then the main class should use the magic __set method etc
Helper class to load:
class syslog_helper{
}
Loader class:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once HELPER_DIR.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$class = new $class;
}
}
Base controller class
class foo{
public $data = [];
public $load;
public function __construct(){
//create the loader instance, pass an instance of the controller (this)
$this->load = new loader($this);
}
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
}
public function __set($key, $value){
//create a dynamic property
$this->data[$key] = $value;
}
public function __get($key){
//get a dynamic property
return $this->data[$key];
}
}
Call it:
(new foo)->bar();
Output:
syslog_helper Object
(
)
Sandbox
As you can see above, $this->syslog_helper gets populated with our helper class just like CI does it.
So it flows in this order:
$foo = new foo - create instance of controller, assign the loader class (with a back reference to the controller) $this->load = new loader($this);
$foo->bar() - call bar() this would be the request function in the controller, such as what the URL routes to.
$foo->load->helper('syslog_helper') - use the load property (instance of loader) to call its helper method, passing the name of the helper class as a string. Helper method should require the class file, and then create an instance of that class. new $class
$this->obj->$class = new $class; - then that instance is assigned to a dynamic property named the same as what was passed in
$this->obj->$class - Controller's __set magic method is triggered, storing instance of helper in Controler->data[$helper]
$foo->syslog_helper() - Controller's __get magic method is triggered returning Controler->data[$helper] or the instance of the helper we just created.
I just made this up, but I am sure CI is similar. You could look in the parent class of the Controllers etc.. and see how they do it.
Hope that makes sense...
One simple improvement you could make for the above code
I think CI does this, is to allow aliasing of the property... like this:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class, $alias = null){
//if no alias default to the class name
if(!$alias) $alias = $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.'helpers/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
Now if we did this in the controller's bar method:
class foo{
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
$this->load->helper('syslog_helper', 'syslog_helper_2');
print_r($this->syslog_helper_2);
}
Output:
syslog_helper Object
(
)
syslog_helper Object
(
)
You now have 2 instances of the helper, one named syslog_helper and the other syslog_helper_2. If we didn't alias them, the second call would simply overwrite the property in the controller leaving us with just one instance.
So you can see above we added a whole lot of flexibility with essentially 1 line of code. Big improvements don't have to be complex.
Sandboxy
Obviously you should flesh this out a bit more. By adding more things, like error checking for classes (files) that don't exist, __unset and __isset magic methods etc... But, this is the basic functionality you wanted.
Similarly you can add, model and library methods with the only real difference being the location. For that I would probably go with the magic __call method instead of 3 functions that do the same.
Implement load->model, load->library and load->helper
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
Sandbox
Implement singletons
A singleton is basically re-using the same instance of the class for future calls, you can implement this with a few more changes to loader:
class syslog_helper{
public $test;
}
class loader{
protected $obj;
protected static $instances = [];
public function __construct($obj){
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//if this is the first time we instantiated [$method][$alias] save it
if(!isset(static::$instances[$method][$alias])){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
static::$instances[$method][$alias] = new $class;
}
//return the saved static instance
$this->obj->$alias = static::$instances[$method][$alias];
}
}
class foo{
public $data = [];
public $load;
public function __construct(){
$this->load = new loader($this);
}
public function bar(){
$this->load->helper('syslog_helper');
print_r('bar::Test before: '.$this->syslog_helper->test."\n");
$this->syslog_helper->test = 'test';
print_r('bar:Test after: '.$this->syslog_helper->test."\n");
}
public function biz(){
$this->load->helper('syslog_helper');
print_r('biz:Test: '.$this->syslog_helper->test."\n");
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key){
return $this->data[$key];
}
}
$foo = new foo;
$foo->bar();
$foo->biz();
Output:
bar::Test before:
bar:Test after: test
biz:Test: test
Sandbox
The important thing here is that, when we call $this->load->helper('syslog_helper'); from biz() in the controller we are getting the same instance of the helper we created previously. You can tell this because the public property I added to the helper retains it's value we set in bar(). You could actually call this anywhere in your code and get the same instance with the same data stored in it, it was just easier (shorter) for examples sake to do it this way.
This is useful if you need the same helper in multiple classes, instead of creating multiple instances you can re-use them. I am not sure if CI does this right off the top of my head... lol
In this case I think it's acceptable to do them as a singleton, if you need a new copy you can just alias it and then that would be a independent instance.
One last thing I should add, is that CI probably doesn't pass the controller instance to the loader class. This is because CI calls the controller from the routing so it already has the instance of the Controller at it's disposal. And as CI is a singleton it can probably be accessed with $CI = get_instance(); inside of loader, so there is no need to pass it along in the the way I showed inside of the CI framework. Basically they just access the same data in a different way.
Cheers!

Calling an external class only once?

I have a class that using an external package to do something
class MyClass
{
public function doSomething($data){
$external = new External();
$external->doSomething($data);
}
}
This class is called from another class, for example:
class MasterClass
{
public function go(){
$data = 'whatever';
$data2 = 'whatever2';
$myClass = new MyClass();
$myClass->doSomething($data);
$myClass->doSomething($data2);
....
}
}
So in my MasterClass I am calling the doSomething function multiple times. WHich creates a new External class multiple times - which is not really necessary.
How can I get around this issue and only create the external class once?
class MyClass
{
protected $external;
public function doSomething($data){
if(!$this->external){
$this->external = new External();
}
$this->external->doSomething($data);
}
}
But read about dependency injection in php.
Move the new External() call into the constructor and store it as a property, then reference that property in the doSomething() method instead of constructing a new instance every time.
Alternatively, if you don't want to always construct a new External whenever you construct a MyClass, you could move the construction into a Lazy Load static method called something like getExternal() in External class.
The first time that method is called it would need to store a new instance of External as a property, but on subsequent calls (when the property is already set) return the same instance. This type of pattern is called Singleton Pattern in Object-Oriented Design Patterns.
You could refer to this link to know more about singleton pattern and how it is implemented.
Pass External class in constructor.
class MyClass
{
private $external;
public function __construct(External $external)
{
$this->external = $external;
}
public function doSomething($data)
{
$this->external->doSomething($data);
}
class MasterClass
{
public function go() {
$data = 'whatever';
$data2 = 'whatever2';
$external = new External();
$myClass = new MyClass($external);
$myClass->doSomething($data);
$myClass->doSomething($data2);
....
}
}

Call an object reference over different methods through ajax in Codeigniter

I am trying to call different methods within same controller written using Codeigniter through separate AJAX calls and create an object of a Third Library in one of those calls and later access the same object (also autoloaded SESSION library) in second method while making second AJAX Call:
Javascript has 2 separate AJAX Calls:
$.post('http://mysite.com/UserController/controllerA',{Param1:x,Param2:y});
$.post('http://mysite.com/UserController/controllerB');
And the controller looks like:
require_once 'FILEPATH/Thirdlibrary.php';
class UserController extends CI_Controller {
protected $service;
public controllerA{
$varA = $this->input->post('Param1');
$varB = $this->input->post('Param2');
$this->service = new ThirdLibray($varA,$varB);
$this->service->startCall();//Thirdlibrary has method startCall() and stopCall
}
public controllerB{
//I want to refer to the same object $this->service here
$this->service->stopCall();
}
Now I know that PHP re-initializes the objects each time its loaded/visited, how could I make this work.How to ovecome such error:
Call to a member function stopCall() on a non-object
(Querying here after making all the efforts searching and coding)
store the object into session and use it.
require_once 'FILEPATH/Thirdlibrary.php';
class UserController extends CI_Controller {
protected $service;
public controllerA{
$varA = $this->input->post('Param1');
$varB = $this->input->post('Param2');
if($this->session->userdata('lib_service'))
{
$this->service =$this->session->userdata('lib_service');
}
else
{
$this->service = new ThirdLibray($varA,$varB);
}
$this->service->startCall();//Thirdlibrary has method startCall() and stopCall
$this->session->set_userdata('lib_service',$this->service); // Why i am saving the object here?, startCall may update some object properties, so that-also will be saved. Otherwise you can have this line in the else part above, after object creation.
}
public controllerB{
//I want to refer to the same object $this->service here
if($this->session->userdata('lib_service'))
{
$this->service =$this->session->userdata('lib_service');
$this->service->stopCall();
// Here after stop if you don't want the object, you can clear from session.
}
}
You can try with Singleton pattern for php. This is the basic code for implement it for your situation but I'm not sure it will work for you. Let's try:
First create a wrraped class for your third party library: ThirdlibraryWrapped.php that you can see more about singleton Best practice on PHP singleton classes
class ThirdLibraryWrapped{
public ThirdLibray thirdLibrary;
public static function Instance($param1=null,$param2 = null)
{
static $inst = null;
if ($inst === null) {
$inst = new ThirdlibraryWrapped($param1,$param2);
}
return $inst;
}
private function __construct($param1,$param2)
{
if($param1!=null && $param2 != null)
thirdLibrary = new ThirdLibrary($param1,$param2);
else
//default constructor of thirdLibrary
thirdLibrary = new ThirdLibrary();
}}
For your controller code:
require_once 'FILEPATH/ThirdlibraryWrapped.php';
class UserController extends CI_Controller {
protected $service;
public controllerA{
$varA = $this->input->post('Param1');
$varB = $this->input->post('Param2');
$this->service = ThirdlibraryWrapped::Instance($varA,$varB);
$this->service->thirdLibrary->startCall();//Thirdlibrary has method startCall() and stopCall
}
public controllerB{
//I want to refer to the same object $this->service here
$this->service = ThirdlibraryWrapped::Instance();
$this->service->thirdLibrary->stopCall();
}}

PHP maintain class state

I'd like to have a library class that maintains state across the same request. My use case is that I want to pass 'messages' to the class, and then call them at any time from a view. Messages can be added from any part of the application.
I had originally done this via static methods, which worked fine. However, as part of the lib, I also need to call __construct and __destruct(), which can't be done on a static class.
Here's a very simple example of what I am trying to do:
class Messages
{
private static $messages = array();
public function __construct()
{
// do something
}
public function __destruct()
{
// do something else
}
public static function add($message)
{
self::$messages[] = $message;
}
public static function get()
{
return self::$messages;
}
}
I can then add messages anywhere in my code by doing
Messages::add('a new message');
I'd like to avoid using static if at all possible (testability). I have looked at DI, but it doesn't seem appropriate, unless I'm missing something.
I could create a class (non-static) instead, but how do I then ensure that all messages are written to the same object - so that I can retrieve them all later?
What's the best way to tackle this?
I looks like you could benefit from using the Singleton pattern - it is designed for an object that must have only one instance throughout a request. Basically, you create a private constructor and a static method to retrieve the sole instance. Here is an example of a singleton that will do what you describe.
<?php
class Messages
{
private static $_instance;
private $_messages = array();
private function __construct() {
// Initialize
}
static public function instance() {
if (! self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function add_message( $msg ) {
$this->_messages[] = $message;
}
public function get_messages() {
return $this->_messages;
}
private function __destruct() {
// Tear-down
}
}
$my_messages = Messages::instance();
$my_messages->add_message( 'How now, brown cow?' );
// ...
$your_messages = Messages::instance();
$msgs = $your_messages->get_messages();
echo $your_messages[0]; // Prints, "How now, brown cow?"
Since the constructor is private, you can only create a Messages object from within a method of the object itself. Since you have a static method, instance(), you can create a new Messages instance from there. However, if an instance already exists, you want to return that instance.
Basically, a singleton is the gatekeeper to its own instance, and it stubbornly refuses to ever let more than one instance of itself exist.
Sounds like you are wanting to do a Singleton class. This will create an instance in one class and allow you to access that same instance in another class. Check out http://www.developertutorials.com/tutorials/php/php-singleton-design-pattern-050729-1050/ for more information.
How about making it a singleton class?
class Messages
{
// singleton instance of Messages
private static $instance;
public function __construct() { ... }
public static function getInstance()
{
if (!self::$instance)
{
self::$instance = new Messages();
}
return self::$instance;
}
}
This would ensure that all your messages get written to the same object, and also allow you to call __construct and __destruct
What you need is the Singleton pattern:
final class Singleton {
// static variable to store the instance
private static $instance = NULL;
// disable normal class constructing
private function __construct() {}
// instead of using the normal way to construct the class you'll use this method
public static function getInstance() {
if (NULL === self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
// disable external cloning of the object
private function __clone() {}
}
// get the instance across some of your scripts
$singleton = Singleton::getInstance();
Sounds a bit like you want a singleton, although as an anti-pattern I'd avoid it.
You could do a full static class where every static member calls a self::_isBuilt(); method to do your construct elements. Destruct is a little trickier.
The best case for your needs might be a normal (non-static) class that you build right away and then access from a global... not super neat, but allows construct/destruct and members, and your statics to use $this which could be helpful. If you don't like the global variable, you could also wrap it in a method (a trick used in JS a fair bit) but it's not really any neater.
As a normal global class:
$myClass=new myClass();
//Access anywhere as:
globals['myClass']->myFunction(..);
Wrapped in a function
function my_class() {
static $var=null;
if ($var===null) $var=new myClass();
return $var;
}
//Access anywhere as:
my_class()->myFunction(..);

How do I maintain a $this reference across two classes in different scripts?

ok, that's a bit odd but here's the situation:
I am working in an MVC context
I have a User class used as a library(User.php)
ANd then I have a controller class that handles input from the user(controller.php)
Here's how it goes:
user registers
registerhandler() is called in controller
registerhandler() calls register() method in User class
register() method creates a User instance with $this and returns a token and echoes a message linking to a verifyhandler() in controller class
verifyhandler calls verify() method in User class
verify() method uses $this to reference the User instance created by register()
boom! $this is not pointing to the same object(created by register() method ) anymore!
I want to maintain that reference yet it seems to take on another value once it is passed to the controller. What can I do about it?
$this always points to the class instance it is used. So $this in your controller and your library will always point to something different and there is little you can do about it.
What you can do is to change the definition of a register function that it accept parameter you want to work with and reference to this parameter instead of using $this. The other option is to define a field in your class and use that field for reference, this way all class methods can be work on the same object.
I think this is more less you want to achieve.
class UserController {
// User library instance
private $User = new User();
private $token = null;
public function registerhandler() {
$this->token = $this->User->register();
}
private function verifyhandler() {
$valid = $this->User->verify($this->token);
...
}
}
class User {
private $sharedData;
public function register() {
// register all common data in $sharedData property
...
return $token;
}
public function verify($token) {
// use shared data and method input to verify the data
...
return true;
}
}

Categories