Using $this when not in object context into the same class - php

I'm trying to build a View class for Smarty templates so I could call templates like in Laravel View::make('template');
But, for first time I'm getting this error. I found plenty of responses fixing the issue but I cannot fix mine. I don't know why so I'm starting to be little bit fed up... hehehehe
This is the class:
class View {
public $engine;
protected $tmpl_folder = 'tmpl';
protected $tmpl_compiled = 'tmpl_c';
protected $cache = true;
protected $force = true;
function __construct() {
$this->engine = new Smarty();
$this->engine->compile_check = $this->cache;
$this->engine->force_compile = $this->force;
$this->engine->template_dir = './' . $this->tmpl_folder . '/';
$this->engine->compile_dir = './' . $this->tmpl_compiled;
}
static function make($t, $args = '') {
if(!empty($args))
$this->engine->assing($args);
$this->engine->display($t.'.tpl');
exit();
}}
Error is launching in method make(), line <b>$this->engine->assing($args)</b>.
I tried to change declaration of $engine variable to public, private, protected and static... nothing...
I declared make() like public, public static, like in the example above and the same with no "static".... nothing...
I tried to change $this-> for self::... nothing...
I don't know what else can I do! Some advice please!

to get this working quickly instantiate your View class as an object like this:
$obj_view = new View();
then make your call to the make() method like this:
$obj_view->make('template');
I'd also declare scope for your methods (functions) (both of them should be public) - you did it for the properties (variables), so why not for the methods as well and remove static from the make()
why are you calling exit() in make()? I hope this is for debug ('Dead programs tell no lies')
assing() made me lol, a great typo! :)
TIP: avoid using bad language in code (in variable names, comments or debug) these are viewable by other developers and can, unintentionally, leak to the end user - it's not professional!

Related

Replace all class instances with stub

I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.

Pondering implementation: Instantiate class based on constant without reflection

Second update
I think I've been approaching this problem from the wrong side of the coin. Would I be correct in assuming that I should be making 'First' an abstract class and just finding a way to reference 'Second' and 'Third' at a later time?
Update
Based on some of the feedback, I have added some content to try and clear up what I would like to do. Something similar to this effect.
I know from just looking at the code below that, it is a waste of performance "if" it did work and because it doesn't, know I am approaching the problem from the wrong angle.The end objective isn't all to uncommon at a guess from some of the frameworks I've used.
I'm more trying to base this particular bit of code on the CodeIgniter approach where you can define (what below) is STR_CLASS_NAME in a config file and then at any point through the operation of the program, use it as i have dictated.
STR_CLASS_NAME = 'Second';
class First {
protected $intTestOne = 100;
public function __construct() {
$strClassName = STR_CLASS_NAME;
return new $strClassName();
}
public function TestOne() {
echo $this->intTestOne;
}
protected function TestThreePart() {
return '*Drum ';
}
}
class Second extends First{
/* Override value to know it's working */
protected $intTestOne = 200;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'roll*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 200.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum roll*'
You may be asking, why do this and not just instantiate Second, well, there are cases when it is slightly different:
STR_CLASS_NAME = 'Third';
class Third extends First{
/* Override value to know it's working */
protected $intTestOne = 300;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'snare*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 300.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum snare*'
Situation
I have a an abstract class which extends a base class with the actually implementation; in this case a basic DB wrapper.
class DBConnector ()
class DBConnectorMySQLi extends DBConnector()
As you can see, MySQLi is the implementation. Now, dependant upon a value in the configuration process, a constant becomes the class name I wish to use which in this case (as shown below builds DBConnectorMySQLi.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
Objective
To have a base class that can be extended to include the implementation
For the code itself not to need know what the name of the implementation actually is
To (in this case) be able to type or use a project accepted common variable to create DBConnectorMySQLi. I.E. $db or something similar. W
Issue
When it comes to actually calling this class, I would like the code to be shown as below. I was wondering whether this is at all possible without the need to add any extra syntax. On a side note, this constant is 100% guaranteed to be defined.
$DBI = new DB_CLASS();
Solution 1
I know it is possible to use a reflection class ( as discussed in THIS QUESTION) and this works via:
$DBI = new ReflectionClass(DB_CLASS);
However, this creates code that is "dirtier" than intended
Solution 2
Start the specific implementation of DBConnectorMySQLi within the constructor function of DBConnector.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
class DBConnector() { public function __construct() { $this->objInterface = new DBConnectorMySQLi(); }
class DBConnectorMySQLi()
This however would result in the need to keep on "pushing" variables from one to the other
Any advice is much appreciate
You can use variables when you instantiate a class.
$classname = DB_CLASS;
$DBI = new $classname();
Source: instantiate a class from a variable in PHP?

PHP Class: Array deinitialized after constructor?

Im making a class in php, but Im having some problems with one of the class variables. I declare a private variable, then in the constructor set it. However, later in the class I have a method that uses that variable. The variable in this case is an array. However, the method says the array is blank, but when I check it in the constructor, it all works out fine. So really the question is, why does my array clear, or seem to clear, after the constructor?
<?php
class Module extends RestModule {
private $game;
private $gamearray;
public function __construct() {
require_once (LIB_DIR."arrays/gamearray.php");
$this->gamearray = $gamesarray;
$this->game = new Game();
$this->logger = Logger::getLogger(__CLASS__);
$this->registerMethod('add', array(Rest::AUTH_PUBLIC, Rest::AUTH_USER, Rest::AUTH_ADMIN), true);
$this->registerMethod('formSelect', array(Rest::AUTH_PUBLIC, Rest::AUTH_USER, Rest::AUTH_ADMIN), false);
}
public function add(){
$game = Utility::post('game');
}
public function formSelect(){
$gamename = Utility::get('game');
$this->$gamearray[$gamename];
}
}
The array is pulled in from another file because the array contains a lot of text. Didn't want to mash up this file with a huge array declared in the constructor. The scrolling would be tremendous. Any explanation would be nice, I like to understand my problems, not just fix em'.
You have a typo:
public function formSelect(){
$gamename = Utility::get('game');
$this->gamearray[$gamename]; // Remove the $ before gamearray
}
Moreover, in your case, include is better than require_once.
If you want to go deeper, you could rewrite $gamearray assignment like this:
// Module.php
$this->gamearray = include LIB_DIR.'arrays/gamearray.php';
// gamearray.php
return array(
// Your data here
);

Working on a PHP framework with an MVC pattern ( New to OOP programming ) Having issues

Before I start just letting you know that I'm new to OOP programming so take it easy on me.
I'm currently working on a PHP framework and I've been able to route my traffic through one entry point. This is the structure below..
index ----> bootstrap ----> router ----> controller
Here's the main controller...
class controller {
protected $_model;
protected $_controller;
protected $_id;
protected $_view;
function __construct($controller,$model,$view,$id=NULL) {
$this->_controller = $controller;
$this->_id = $id;
$this->_model = $model;
$this->_view = $view;
$this->$model = new $model;
$this->_view = new view;
}
function filter($data) {
$data = trim(htmlentities(strip_tags($data)));
if (get_magic_quotes_gpc())
$data = stripslashes($data);
$data = mysql_real_escape_string($data);
return $data;
}
function set($name,$value) {
$this->_view->set($name,$value);
}
function __destruct(){
$this->_view->render();
}
}
In my construct function it's suppose to set the...
$this->$model = new $model; and $this->_view = new view;
But when I run either $this->_view->render(); or $gamerequests_array = $this->main_model->tab_query($tab);
It says the object doesn't exist. Here's the exact error message I get.
Notice: Undefined property: main_controller::$main_model and
Fatal error: Call to a member function tab_query() on a non-object
So at this point I know that the problem is the framework setting up the new objects. I've been trying to figure this out and at this point I need some help.
If anyone needs extra code feel free to request it, I'll post it.
From this line:
$this->$model = new $model;
It appears your passing in a string and using it both as the property name and the class name. Perhaps you're passing in a name that is not exactly main_model. Though variable/property names are case-sensitive, class names are not. So, they're may be a case mismatch. Pass $this through get_object_vars to see what properties are available. Keep in mind that dynamically adding properties in this way will make them public.
These lines should be examined as well:
$this->_view = $view;
$this->_view = new view;
You're overwriting the property. I'm sure this is unintentional and may be part of the problem you're experiencing. Perhaps you're trying to use the same approach as you did with the controller (store name, use for instantiation and access).
$this->$model = new $model;
This could only possibly work if the value for $model also happens to be a property that has already been set. Which means it can only contain the values '_controller', '_id', '_model' and '_view' or it will kick out an error (Notice: Undefined property: main_controller::$main_model and Fatal error: Call to a member function tab_query() on a non-object)- even if it has one of those values, it will still overwrite whatever you set the property as.
If you are trying to dynamically set the view as something, you may wish to pass $view into the View constructor as an argument:
$this->_view = new view($view);
You can further extend that by setting different behaviours for different values of $view through inheritance via either a global function with a case switch returning the appropriate object or a method of the View class.

PHP - mysql_query() inside a static function of a class

The Situation
I have a table in a DB that contains job types, basically defined by a label and a price. I'm trying to do a simple SELECT * FROM jobtype but I can't seem to get it to work, although I've use the same block over and over again in the rest of the code. The main difference here, is that it is a singleton trying to execute the function.
The problem is that as soon as I uncomment the line $rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error()); the page will stop loading at this particular point in the code.
The Code
Following is the code of my function getJobTypes():
require_once('Connections/vtconnection.php');
class JobTypes extends Singleton{
static private $job_types;
public static function getJobTypes(){
if (self::$job_types == null){
echo 'DEBUG: For now, $job_types is NULL.'."\n";
mysql_select_db($database_vtconnection, $vtconnection);
$query_job_types = 'SELECT * FROM jobtype';
$rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error());
while ($rs_row = mysql_fetch_assoc($rs_job_types)){
// let the job type identifier in the db be its index in our array
self::$job_types[$rs_row['id']]['label'] = $rs_row['label']; // job type label
self::$job_types[$rs_row['id']]['price'] = $rs_row['price']; // job type price
}
if (self::$job_types != null) echo 'DEBUG: $job_types has been populated.'."\n";
}
return self::$job_types;
}
}
Which I am calling like so:
$jt = JobTypes::getJobTypes();
Here is my singleton pattern:
class Singleton{
private static $instances = array();
final private function __construct(){
}
final public function __clone(){
trigger_error('You don\'t clone a singleton!', E_USER_ERROR);
}
final public static function getInstance(){
$c = get_called_class();
if(!isset(self::$instances[$c])){
self::$instances[$c] = new $c;
}
return self::$instances[$c];
}
}
I have turned the problem in my head, commented everything inside the getJobtypes() function and uncommented step by step. I found out the problem does happen with the mysql_query() line, just can't seem to work out why. Is something obviously wrong in my code?
Solved
As suggested here, I used global $vtconnection,$database_vtconnection; at the start of my static function and all went smooth. It is not an optimal solution but it pointed out the scope issue which I will now try to resolve.
I also got rid of the singleton pattern.
Well the most obvious thing is $database_vtconnection $vtconnection are defined nowhere in your getJobTypes function. If they're part of the class, you need $this (object) or self (static) references. More likely it looks like you're trying to use global variables, in which case you have to pull them into the scope of the function.
mysql_select_db can auto-connect (if $vtconnection isn't supplied) but only if it knows how - there's a previous connection (or possible INI config with db/host/user/pass).
To pull them into scope you need to add the line at the beginning of the function:
global $vtconnection,$database_vtconnection;`
... Or use the $GLOBALS superglobal array:
mysql_select_db($GLOBALS["database_vtconnection"],$GLOBALS["vtconnection"]);
For the record using globals isn't a particularly good solution.

Categories