Smarty custom plugin to use base class - php

Here is my class that gets called on each page:
class ActionHandler {
var $smarty = NULL;
public function __construct() {
if($this->smarty == NULL){
$this->smarty = new Smarty();
$this->smarty->template_dir = TEMPLATE_DIR;
$this->smarty->compile_dir = COMPILE_DIR;
}
}
public function do_something($page_id) {
return $page_id + 1;
}
}
Now I have a custom plugin for smarty that I want to use in my template:
function smarty_function_something($params, &$smarty) {
return ActionHandler::do_something($params['page_id']);
}
However I get Fatal error: Using $this when not in object context.
I see why but don't know how to get around this. Any ideas?

Try making the do_something a static member of ActionHandler
class ActionHandler {
public static $smarty = NULL;
public function __construct()
{
if($this->smarty == NULL)
{
$this->smarty = new Smarty();
$this->smarty->template_dir = TEMPLATE_DIR;
$this->smarty->compile_dir = COMPILE_DIR;
}
}
public static function do_something($page_id)
{
return $page_id + 1;
}
}
As your trying to access a non static method i *think that the __construct gets executed before the method is available, but as you have not created an instance of the object, the keyword $this does not exists.
you have to create specific static methods. if your going MyObject::SomeMethod($param)
you should also take a look at Object Auto Loading and Auto Initializing objects via static methods.
also you don't need to specifically define the value to public static $smarty = NULL; as Null is a default value of any new variable, just do
public static $smarty;
going a little more indepth with your problem you should add a singleton method like so..
class ActionHandler
{
public static $smarty;
public static $singleton;
public function __construct()
{
if($this->smarty == NULL)
{
$this->smarty = new Smarty();
$this->smarty->template_dir = TEMPLATE_DIR;
$this->smarty->compile_dir = COMPILE_DIR;
}
}
public static GetSingleton()
{
if(self::$singleton == null)
{
self::$singleton = new ActionHandler();
}
return self::$singleton;
}
public static function do_something($page_id)
{
$_this = self::GetSingleton();
return $page_id + 1;
}
}

You omitted a few pieces of code: instantiation of either the Smarty or ActionHandler object, registration of the template function, template content, and Smarty::display() call, but in my own testing your code works fine. In none of your code do you attempt to use $this while not in an object context.
If you have additional code to post (preferably, the full reduction that still triggers the error) that may help with debugging.
smarty-test.php:
<?php
include 'Smarty.class.php';
class ActionHandler {
var $smarty = NULL;
public function __construct() {
if($this->smarty == NULL){
$this->smarty = new Smarty();
$this->smarty->template_dir = __DIR__ . '/t';
$this->smarty->compile_dir = __DIR__ . '/tc';
$this->smarty->plugins_dir = __DIR__ . '/plugins';
}
}
public function do_something($page_id) {
return $page_id + 1;
}
}
$ah = new ActionHandler;
$ah->smarty->display('index.tpl');
plugins/function.something.php:
<?php
function smarty_function_something($params, &$smarty) {
return ActionHandler::do_something($params['page_id']);
}
t/index.tpl:
Test: {something page_id=1}
Output:
Test: 2

Related

PHP OOP: Create global array of messages

I am trying to display an array of messages at the end of my PHP class. My message handler is working, but only if I "add_message" from within the main parent class and not if I call this function from within a child class. Sorry if this is vague but was not sure how to word the question.
TLDR; How can I add a message from within class Example?
MAIN PARENT CLASS
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->messages->display_messages();
}
}
MESSAGE HANDLER
class Message_Handler {
public function __construct() {
$this->messages = array();
}
public function add_message( $msg ) {
$this->messages = $this->add( $this->messages, $msg );
}
private function add( $messages, $msg ) {
$messages[] = $msg;
return $messages;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( $this->messages );
}
}
EXAMPLE CLASS
class Example {
public function fire_instance() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'Hello Universe!' ); // This message is NOT being displayed...
}
}
Because you want to keep the messages around different object, you should pass the object or use a static variable.
I would use a static variable like so:
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
// renamed the message handler variable for clarity
$this->message_handler = new Message_Handler();
$this->message_handler->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->message_handler->display_messages();
}
}
class Message_Handler {
// use a static var to remember the messages over all objects
public static $_messages = array();
// add message to static
public function add_message( $msg ) {
self::$_messages[] = $msg;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( self::$_messages );
}
}
class Example {
public function fire_instance() {
// new object, same static array
$message_handler = new Message_Handler();
$message_handler->add_message( 'Hello Universe!' );
}
}
// testing...
new Init();
new Init();
$init = new Init();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->run();
Although global variables might not be the best design decision, you have at least two approaches to achieve what you want:
Use singleton.
Nowadays it is considered anti-pattern, but it is the simplest way: make message handler a singleton:
class MessageHandler
{
private static $instance;
private $messages = [];
public static function instance(): self
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
}
public function addMessage($message): self
{
$this->messages[] = $message;
return $this;
}
public function messages(): array
{
return $this->messages;
}
}
Then instead of creating a new instance of MessageHandler access it via the static method MessageHandler::instance(). Here is a demo.
Use DI container to inject the same instance (that is created once and held in the container) into all instances that need to access it. This approach is more preferable, but harder to implement in the project where there is no DI container available in the first place.

Cannot make non static method - FATAL ERROR

I was working on a PHP web application. I used a new datagrid from gurrido.net and it worked well on the local but when I upload it to the server, I get the following error:
Fatal error: Cannot make non static method Base::getClassName() static
in class Singletons in /var/www/reskb/phpinc/Singletons.class.php on
line 84
In my old version where I didn't use the grid, I got it working. Here is my code of singletons.class.php file:
<?
class Singletons extends Base {
var $objects = array();
function getClassName() {
return 'Singletons';
}
function _instance() {
static $_instance = NULL;
if ($_instance == NULL) {
$className = Singletons::getClassName();
$_instance = new $className();
}
return $_instance;
}
function put($object) {
$self = Singletons::_instance();
$className = $object->getClassName();
$self->objects[$className] = $object;
}
function get($className) {
$self = Singletons::_instance();
if(!empty($self->objects[$className]))
return $self->objects[$className];
else return '';
}
}
Singletons::_instance();
?>
You should call function getClassName using object or define getClassName as static. –
<?php
class Singletons extends Base {
var $objects = array();
static function getClassName() {
return 'Singletons';
}
static function _instance() {
static $_instance = NULL;
if ($_instance == NULL) {
$className = Singletons::getClassName();
$_instance = new $className();
}
return $_instance;
}
function put($object) {
$self = Singletons::_instance();
$className = $object->getClassName();
$self->objects[$className] = $object;
}
function get($className) {
$self = Singletons::_instance();
if(!empty($self->objects[$className]))
return $self->objects[$className];
else return '';
}
}
Singletons::_instance();
?>
You're getting the error because you're trying to call your function statically when it isn't a static method (function).
You need to specify the function as static:
static function getClassName() {
return 'Singletons';
}
That goes for every method you'd like to call statically.
If you declare a function as abstract in an abstract super class, then attempt to define it as static in a child class, you will get a fatal error. You may want to think about the way your class hierarchy is structured, as your only option will be to remove the abstract function declaration from the super class.

Inheritance in PHP - Creating child instance and calling parent method

I have something like this:
class MyParent {
protected static $object;
protected static $db_fields;
public function delete() {
// delete stuff
}
public static function find_by_id($id = 0) {
global $database;
$result_array = self::find_by_sql("SELECT * FROM " . static::$table_name . " WHERE id=" . $database -> escape_value($id) . " LIMIT 1");
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql = "") {
global $database;
// Do Query
$result_set = $database -> query($sql);
// Get Results
$object_array = array();
while ($row = $database -> fetch_array($result_set)) {
$object_array[] = self::instantiate($row);
}
return $object_array;
}
private static function instantiate($record) {
$object = self::$object;
foreach ($record as $attribute => $value) {
if (self::has_attribute($attribute)) {
$object -> $attribute = $value;
}
}
return $object;
}
}
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = new TheChild;
}
}
$child= TheChild::find_by_id($_GET['id']);
$child->delete();
I get this: Call to undefined method stdClass::delete() referring to the last line above. What step am I missing for proper inheritance?
You never actually instanciate the TheChild class, which should be done by
$var = new TheChild();
except in TheChild constructor itself.
So, the static $object field is never affected (at least in your example), so affecting a field to it (the line $object -> $attribute = $value; ) causes the creation of an stdClass object, as demonstrated in this interactive PHP shell session:
php > class Z { public static $object; }
php > Z::$object->toto = 5;
PHP Warning: Creating default object from empty value in php shell code on line 1
php > var_dump(Z::$object);
object(stdClass)#1 (1) {
["toto"]=>
int(5)
}
This object does not have a delete method.
And as said before, actually creating a TheChild instance will result in an infinite recursion.
What you want to do is this, probably:
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = $this;
}
}
Edit: Your updated code shows a COMPLETE different Example:
class MyParent {
protected static $object;
public function delete() {
// delete stuff
}
}
class TheChild extends MyParent {
function __construct() {
self::$object = new TheChild;
}
}
$child = new TheChild;
$child->delete();
Calling "Child's" Constructor from within "Child's" Constructor will result in an infinite loop:
function __construct() {
self::$object = new TheChild; // will trigger __construct on the child, which in turn will create a new child, and so on.
}
Maybe - i dont know what you try to achieve - you are looking for:
function __construct() {
self::$object = new MyParent;
}
ALSO note, that the :: Notation is not just a different Version for -> - it is completely different. One is a Static access, the other is a access on an actual object instance!

What's wrong in this singleton class

I've 3 classes. [1]Singleton [2]Load [3]Dashboard . In Load class there is one method called 'model()'. Where i'm initializing data for singleton object by using this code.
$obj = Singleton::getInstance();
$obj->insertData('email', 'mail#domain.com');
Again, from Dashboard class there is one method called 'show()' from where i'm trying to print the Singleton object data. But, here i can see all the data of Singleton object except the data which has been initialized by 'model' method of 'Load' class.
Here is my full code...
<?php
//---Singletone Class---
class Singleton
{
// A static property to hold the single instance of the class
private static $instance;
// The constructor is private so that outside code cannot instantiate
public function __construct() {
if(isset(self::$instance))
foreach(self::$instance as $key => &$val)
{
$this->{$key} = &$val;
}
}
// All code that needs to get and instance of the class should call
// this function like so: $db = Database::getInstance();
public static function getInstance()
{
// If there is no instance, create one
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Block the clone method
private function __clone() {}
// Function for inserting data to object
public function insertData($param, $element)
{
$this->{$param} = $element;
}
}
//---LOAD class---
class Load
{
function __construct()
{
$obj = Singleton::getInstance();
$obj->insertData('country', 'INDIA');
}
function model()
{
$this->name = 'Suresh';
$obj = Singleton::getInstance();
$obj->insertData('email', 'mail#domain.com');
}
function msg()
{
return('<br><br>This message is from LOAD class');
}
}
$obj = Singleton::getInstance();
$load = new load();
$obj->load = $load;
//---Dashboard Class---
class Dashboard extends Singleton
{
function __construct()
{
parent::__construct();
}
function show()
{
echo "Default data in current Object";
echo "<br>";
print_r($this);
echo $this->load->msg();
$this->load->model();
echo "<br><br>Data in current Object after post intialization";
echo "<br>";
print_r($this);
}
}
$dashboard = new dashboard();
$dashboard->show();
If your singleton was truly a singleton then the update would have worked. I'm suspecting that you may have multiple instances of the singleton class that is initialized.
Edit:
Also its not a good idea to inherit from a true singleton class.
You need to remove the inheritance that Dashboard has on Singleton
Edit:
Best practice on PHP singleton classes
I don't like your direct access to an object like an array. This one is a better approach [see here]:
You should call it like this:
$obj = Singleton::getInstance();
$load = new Load();
$obj->insertData( 'load', $load );
Implementation of Singleton:
class Singleton
{
// A static property to hold the single instance of the class
private static $instance;
// my local data
protected $_properties;
// You might want to move setter/getter to the end of the class file
public function __set( $name, $value )
{
$this->_properties[ $name ] = $value;
}
public function __get( $name )
{
if ( ! isset( $this->_properties[ $name ] )) {
return null;
}
return $this->_properties[ $name ];
}
// No need to check, if single instance exists!
// __construct can only be called, if an instance of Singleton actually exists
private function __construct() {
$this->_properties = array();
foreach(self::$instance as $key => &$val)
{
$this->_properties{$key} = &$val;
}
}
public static function getInstance()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Function for inserting data to object
public function insertData($param, $element)
{
$this->_properties{$param} = $element;
}
// Block the clone method
private function __clone() {}
}

PHP OOP - Wrong object returned

with the follow code:
<?php
class Loader {
private static $instances;
function __construct($class = null) {
return self::instance($class);
}
public static function instance($class) {
if(!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
class Core {
}
$core = new Loader('Core');
print_r($core);
?>
my print_r() return the object Loader instead the object Core, which is instantiated after Loader is constructed.
Thanks for help!
Hm ?
If you do
$core = new Loader('Core');
Then $core is going to be an instance of Loader...
PS : constructors don't return a value.
You don't need to instantiate Loader at all.
Do this :
<?php
class Loader {
private static $instances;
public static function instance($class) {
if(!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
class Core {
}
$core = Loader::instance('Core');
print_r($core);
Or you could do a much simpler :
<?php
function Load($class)
{
static $instances;
if(!isset($instances[$class]))
$instances[$class] = new $class();
return $instances[$class];
}
class Core {
}
$core = Load('Core');
print_r($core);

Categories