WordPress PHP Extended Classes Refer to 'Wrong' Static Variables - php

I have a WordPress plugin with 2 extended classes, Area and Loc, each of which has a helper function Get. Each class has static variables for table_name. If I call Loc::Get($id) directly, it works as expected. If, however, I call Loc::Get($id) from Area, it uses the table_name from Area rather than Location.
Can anyone explain how to correct this? Regards,
class _Base {
function Get($id) {
$instance = new self();
$sql = "SELECT * FROM " . static::$table_name . " WHERE id=$id";
return $sql;
}
}
class Area extends _Base {
static $table_name = "Area";
function getLoc($id) {
$sql = Loc::Get($id);
return $sql;
}
}
class Loc extends _Base {
static $table_name = "Loc";
}
$sql = Area::Get(1); // -> "SELECT * FROM **Area** WHERE id=1"
$sql = Loc::Get(1); // -> "SELECT * FROM **Loc** WHERE id=1"
$sql = $area->GetLoc(1); // -> "SELECT * FROM **Area** WHERE id=1"

I think your problem is you run NON static method Get as static. When i changed it then all start work fine. Try my code below:
<?php
class _Base {
static function Get($id) {
$instance = new self();
$sql = "SELECT * FROM " . static::$table_name . " WHERE id=$id";
return $sql;
}
}
class Area extends _Base {
static $table_name = "Area";
function getLoc($id) {
return Loc::Get($id);
}
}
class Loc extends _Base {
static $table_name = "Loc";
}
var_dump(Area::Get(1));
var_dump(Loc::Get(1));
var_dump((new Area)->getLoc(1));

First
Function Get is not static by definition
function Get($id) {
Second
$area is not defined

Related

How to Access Data from One Class in multiple Children Classes

I'm having a bit of trouble in designing my classes in php.
As you can see in my Code, i want to have one Class instance and having more classes as children which "talk" from one to another. im getting the logged user and get all his information stored to a variable. In my other Classes i recently need to get this UserData.
Any help and Ideas are welcome :)
class Factory
{
private $UserData;
public function Factory()
{
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
private function getLoggedUserData( $user )
{
$result = DB::query( "SELECT * FROM users WHERE user='$user' LIMIT 1" );
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks()
{
// how to call that class, without instancing it over and over again
MyOtherClass -> getMyTasks();
}
}
class MyOtherClass
{
public function getMyTasks()
{
// how to access the "global" variable
$result = DB::query( "SELECT * FROM tasks WHERE userID=" . $UserData['userID'] . " LIMIT 1" );
// doSomething ($result);
}
}
class DB
{
private static $mysqli;
public static function connect()
{
$mysqli = new mysqli(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB);
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->conect_errno . ')' . $mysqli->connect_error);
}
mysqli_set_charset($mysqli, 'utf8');
self::$mysqli = $mysqli;
}
public static function query( $query )
{
$result = self::$mysqli->query( $query );
if ( self::$mysqli->error ) {
error_log("QUERY ERROR: " . self::$mysqli->error);
error_log("QUERY: " . $query);
}
return $result;
}
}
$Factory = new Factory();
OK, here goes a simple trivial approach to your problem
Mind you, this is not complete. Gimme some feedback if this is closing in on what you'd expect
your classes changed a bit
<?php
class Factory {
private $UserData;
private $UserTask;
public function Factory() {
DB::connect();
$this->getLoggedUserData($_SERVER['REMOTE_USER']);
}
private function getLoggedUserData($user) {
$result = DB::query('SELECT * FROM users WHERE user="'.$user.'" LIMIT 1');
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks() {
// how to call that class, without instancing it over and over again
if (!isset($this->UserTask)) $this->UserTask = new MyOtherClass($this->UserData);
return $this->UserTask->getMyTasks();
}
}
class MyOtherClass {
private $UserData;
public function __construct($userData) {
$this->userData = $userData;
}
public function getMyTasks() {
// how to access the "global" variable
$task = DB::query('SELECT * FROM tasks WHERE userID='.$this->UserData['userID'].' LIMIT 1');
return $this->performTask($task);
}
public function performTask($task) {/* doSomething(); */}
}
// usage is not complete, waiting for some extra input
$factory = new Factory();
$taskResults = $factory->getMyTasks();
Any input on how to improve this is very welcome
edit following comments
Let's take a look at how you can solve the problem of having to share instances between different "apps" in your code
the singleton approach: an instance is created on the first call, all subsequent calls are passed the single instance
the registry pattern: an object created at the start of the script picks up all initialized requirements and stores them. If any "app" needs the basic set of services (it's not standalone), then pass the registry object to it's initializer/constructor.
I hope I understood your comments well enough, if not feel free to ask and correct me
Hard to say what would be best for you when i dont know more about the scale of your application etc.
Anyway the simplest way is something like this:
$otherClass = new MyOtherClass();
$Factory = new Factory($otherClass);
Class Factory
class Factory
{
private $UserData;
private someClass;
public function Factory(&$someClass)
{
$this->someClass = $someClass;
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
...
Usage
$this->someClass->getMyTasks();
But in case you only want access to the methods/variables of the parent, then yes extend the class.

class within another class

I am new to PHP OOP and would like to try to nest several classes within another class, to latter call them like so:
$sql = new SQL();
$sql->Head()->Description($_SESSION['page']);
//OR
$sql->Head()->Keywords($_SESSION['page'])
//OR
$sql->Body()->Clients($_SESSION['client'])
//ETC
$query = $sql->Run(); // equivalent to mysql_query("...");
As you can guess, I run into some problems and ended with this poor code:
<?php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
//This file contains $db['host'], $db['user'], etc...
class SQL {
public $sql;
public function __construct() {
global $db;
}
public class Head() {
public function Description($page) {
return "SELECT * FROM `$db['database']`.`desciption` WHERE `page` = '$page'";
}
public function Keywords($page) {
return "SELECT * FROM `$db['database']`.`keywords` WHERE `page` = '$page'";
}
}
}
$sql = new SQL();
echo $sql->Head()->Description('home'); //For testing
Is it possible to nest classes in PHP?
If so, how is it done?
What you are trying to do is called Encapsulation. Try google search on PHP encapsulation to learn more.
Here is a code example from http://www.weberdev.com/get_example.php3?ExampleID=4060,
<?php
class App {
private static $_user;
public function User( ) {
if( $this->_user == null ) {
$this->_user = new User();
}
return $this->_user;
}
}
class User {
private $_name;
public function __construct() {
$this->_name = "Joseph Crawford Jr.";
}
public function GetName() {
return $this->_name;
}
}
$app = new App();
echo $app->User()->GetName();
?>
I'm assuming that database.php is a database class. In that case you could do something like this.
head.php
Class Head{
private $_db;
private $_dbName;
public function __construct($db, $dbName){
$this->_db = $db;
$this->_dbName = $dbName;
}
public function Description($page) {
$results = $this->_db->query("SELECT `text` FROM `$this->_dbName`.`description` WHERE `page` = '$page'");
return '<meta name="description" content="' . $results['text'] . '">';
}
public function Keywords($page) {
$results = $this->_db->query("SELECT * FROM `$this->_dbName`.`keywords` WHERE `page` = '$page'");
$keywords = array();
foreach($results as $result){
array_push($keywords, $result['word']);
}
return '<meta name="keywords" content="' . implode(',', $keywords) . '">';
}
}
sql.php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
// Require head class file
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/head.php');
Class SQL{
public $Head;
public function __construct($dbName){
global $db;
$this->Head = new Head($db, $dbName);
}
}
You would then pass the name of the database into the SQL class (which propogates through to the Head class).
// Require the sql class file
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/sql.php');
$sql = new SQL('mydatabase');
echo $sql->Head->Description('home');
Again note that your database class might not return results the way I'm using them here. You will have to modify this to work with your particular database class.
Try it like this
<?php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
//This file contains $db['host'], $db['user'], etc...
class SQL {
public $sql;
private $_head;
public function __construct() {
global $db;
$_head = new HeadClass();
}
public function Head() {
return $this->_head;
}
}
class HeadClass { // Class cannot have a public access modifier
public function Description($page) {
return "SELECT * FROM `" . $db['database'] . "`.`desciption` WHERE page = $page";
}
public function Keywords($page) {
return "SELECT * FROM `" . $db['database'] . "`.`keywords` WHERE page = $page";
}
}
$sql = new SQL();
echo $sql->Head()->Description('home.html');
?>
I am moving the class declaration outside the class and creating an instance of the class with in SQL. This is then made available via the Head() function.
Note: For body you will need to create a separate class and use a reference in the SQL class to it like I have done for head.

PHP singleton pattern

Hello there i want to learn a singleton pattern in php,
i have a class:
class Database
{
private static $instance;
private function __construct()
{
}
public static function getInstance()
{
if (!self::$instance)
{
self::$instance= new Database();
}
return self::$instance;
}
public function query($table)
{
$this->query = 'select * from $table';
}
public function result()
{
echo $this->query;
}
}
$db = Database::getInstance();
and now , is it posible to call the result() method and print the value set by the query() which is "select * from $table" using a singleton?
i want my code in something like:
$db->query('user_tb')->result();
//output
select * from user_tb;
Update:
To be able to call it like:
$db->query('user_tb')->result();
You need to put return $this; in method you want to chain, in this case your query method:
public function query($table)
{
$this->query = "select * from $table";
return $this;
}
Now you can call it like : $db->query('user_tb')->result();
Working Example
-------------------------------------------------------------------------------------------
First modify in your query() method:
$this->query = 'select * from $table';
To:
$this->query = 'select * from ' . $table;
since inside single quotes, variables are not parsed.
And then define $query at class level like this:
class Database {
private static $Instance;
private $query = '';
// your more code
}
And then you can run this to get it:
$db = Database::getInstance(); // get class instance
$db->query('user_tb'); // set $query var
$db->result(); // get $query var
Result:
select * from user_tb
Working Example
To use method chaining, make sure all functions you want to chain return $this.
Then you can do DB::getInstance()->query()->result();.=
So query at least needs to return $this.
Also, you forgo any error handling by return parameter, so generally if you use method chaining you need to use exception handling to deal with errors.
As in, you can't do
if(!$db->query) {
error_log('bleh');
}

How to display data on the page without putting code to display data in class definition

I have function with simple sql query to list tickets on the page:
class DBQuery
{
function displayTicketsList(){
$this->dbConnection();
$query = "SELECT * FROM tickets";
$this->result = mysql_query($query);
$this->dbClose();
}
.
.
.
}
How to display data on the page without putting code to display in class definition.
Your question isn't that clear, but I think you mean something like this:
class DBQuery
{
function displayTicketsList(){
$this->dbConnection();
$query = "SELECT * FROM tickets";
$this->result = mysql_query($query);
$this->dbClose();
return $this->result;
}
.
.
.
}
and then in your "view", or whatever:
$myClass=new DBQuery;
$foo=$myClass->displayTicketsList();
print_r($foo);

nesting classes in php

here is my sample class to why i want to nest.
include("class.db.php");
class Cart {
function getProducts() {
//this is how i do it now.
//enter code here`but i dont want to redeclare for every method in this class.
//how can i declare it in one location to be able to use the same variable in every method?
$db = new mysqlDB;
$query = $db->query("select something from a table");
return $query
}
}
Take advantage of properties.
class Cart {
private $db;
public function __construct($db) {
$this->$db = $db;
}
public function getProducts() {
$query = $this->db->query( . . .);
return $query;
}
}
You'll create the database object outside of your class (loose coupling FTW).
$db = new MysqlDb(. . .);
$cart = new Cart($db);
Isolate the common code to each method/function into another private internal method/function.
If you need to have it run once automatically for the object when it's created, this is what __construct is for.
You could have something like this
<?php
class cart
{
protected $database;
function __construct()
{
$this->database = new mysqlDB;
}
function getProducts()
{
$this->database->query("SELECT * FROM...");
}
}
?>
__construct is the function that is called when you instantiate a class.

Categories