I couldn't acces variable in child class - php

First, Thanks a lot
My problem is that i have Page which (indexStatic.php) have content variable return and affect some head tags like include js files but when i start file is give this message:
I want to access variables which are displayControler.php in indexStatic.php
<b>Fatal error</b>: Uncaught exception 'Exception' with message 'js doesn't exist in this class' in C:\webServer\htdocs\blabla\admin\AdminPanel\core\modelAbstract.php:25
Stack trace:
#0 C:\webServer\htdocs\blabla\admin\AdminPanel\static\indexStatic.php(2): ModelAbstract->__set('js', '<script src="re...')
#1 C:\webServer\htdocs\blabla\admin\AdminPanel\core\tools.php(14): include('C:\webServer\ht...')
#2 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(33): tools->includeFile('/static/indexSt...')
#3 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(28): displayControler->getContentHtml('/static/indexSt...')
#4 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(18): displayControler->setUrlTofunc(NULL)
#5 C:\webServer\htdocs\blabla\admin\AdminPanel\index.php(17): displayControler->showHtml()
#6 {main}
thrown in <b>C:\webServer\htdocs\blabla\admin\AdminPanel\core\modelAbstract.php</b> on line <b>25</b><br />
indexStatic.php is included in displayControler.php lets see files :
indexStatic.php File :
<?php
$this->js ='<script src="resources/scripts/editablegrid-2.0.1.js"></script> ';
$this->js .='<script src="resources/scripts/jquery-1.7.2.min.js" ></script>';
$this->js.='<script src="resources/scripts/demo.js" ></script>';
return'<div class="content-box"><!-- Start Content Box -->
<script type="text/javascript">
window.onload = function() {
datagrid = new DatabaseGrid();
};
</script>
</div>';
?>
displayControler.php
class displayControler extends ModelAbstract
{
protected $js;
protected $tools;
protected $title;
protected $meta;
protected $content='';
protected $urls=array(
'main'=>'static/aboutStatic.php'
);
public function __construct(){
$this->setTitle(defaultVar::$static['title']);
}
public function showHtml(){
$this->setUrlTofunc(#$_GET['cont']);
include 'static/adminStatic.php';
}
public function setUrlTofunc($url){
$this->tools=new tools();
if(isset($url) and isset($this->urls[$url]) ){
$this->getContentHtml($this->urls[$url]);
}else{
$this->getContentHtml(defaultVar::$static['filePath']);
}
}
public function getContentHtml($url){
$catch = $this->tools->includeFile($url);
$this->setContent($catch);
}
}
and modelAbstract.php is have :
abstract class ModelAbstract{
public function __call($name, $args){
$methodPrefix = substr($name, 0,3);
$methodProperty = strtolower($name[3]).substr($name,4);
switch ($methodPrefix){
case "set":
if (count($args) == 1)
$this->__set($methodProperty,$args[0]);
else
throw new \Exception("magic method only supports one argument");
break;
case "get":
return $this->__get($methodProperty);
break;
default:
throw new Exception("magic methods only supported for get and set");
}
}
public function __set($name,$value){
if (property_exists($this, $name))
$this->$name = $value;
else
throw new \Exception("$name doesn't exist in this class");
}
public function __get($name){
if (property_exists($this, $name))
return $this->$name;
else
throw new \Exception("$name doesn't exist in this class");
}
}

The $js property is protected so you can not access it directly. You have magics in the Model class which make the getter and setter for your properties.
This means it will go through the magic call. Try using $this->setJs("") instead of $this->js = "".

Related

Fatal Error - Call to undefined method "Customersss::throwError()"

I created a class and try to call in a method, but however in my error log, I am getting response "Uncaught Error: Call to undefined method Customersss::throwError()", please what am I doing wrong, because I know I have created throwError(), I can't seem to be able to access the class.
Firstly the class trying to call object
$this->throwError(INVALID_DATA_TTT, "Invalid shipping fee"); //WHERE I SUSPECT ERROR IS BEING GENERATED
The full error
PHP Fatal error: Uncaught Error: Call to undefined method
Customersss::throwError() in
/home/osconliz/public_html/Osconlizapicall/customers.php:276 Stack
trace:
#0 /home/osconliz/public_html/Osconlizapicall/api.php(177): Customersss->insertNewDelivery()
#1 [internal function]: Api->create_insert_new_delivery()
#2 /home/osconliz/public_html/Osconlizapicall/rest.php(42): ReflectionMethod->invoke(Object(Api))
#3 /home/osconliz/public_html/Osconlizapicall/index.php(4): Rest->processApi()
#4 {main} thrown in /home/osconliz/public_html/Osconlizapicall/customers.php on line 276
customers.php
require_once('constants.php');
require_once('rest.php');
class Customersss {
private $id;
private $shipping_fee;
private $pickup_fee;
function setId($id){ $this->id = $id; }
function getId() { return $this->id; }
function setShipping_fee($shipping_fee){ $this->shipping_fee = $shipping_fee; }
function getShipping_fee() { return $this->shipping_fee; }
function setPickup_fee($pickup_fee){ $this->pickup_fee = $pickup_fee; }
function getPickup_fee() { return $this->pickup_fee; }
public function __construct(){
$db = new DbConnect();
$this->dbConn = $db->connect();
}
public function insertNewDelivery(){
if ($this->shipping_fee == ""){
$this->throwError(EMPTY_PARAMETER, "Empty shipping fee");
exit();
}
if ($this->shipping_fee == ""){
$this->throwError(INVALID_DATA_TTT, "Invalid shipping fee");
exit();
}
}
}
rest.php
require_once('constants.php');
class Rest {
protected $request;
protected $serviceName;
protected $param;
public function processApi(){
$api = new API;
$rMethod = new reflectionMethod('API', $this->serviceName);
if(!method_exists($api, $this->serviceName)){
$this->throwError(API_DOST_NOT_EXIST, "API does not exist");
}
$rMethod->invoke($api);
}
public function throwError($code, $message){
header("content-type: application/json");
$errorMsg = json_encode(['error' => ['status'=>$code, 'message'=>$message]]);
echo $errorMsg; exit;
}
}
constants.php
define('INVALID_DATA_TTT', 350);
define('EMPTY_PARAMETER', 404);
define('API_DOST_NOT_EXIST', 400);
define('ACCESS_TOKEN_ERRORS', 500);
api.php
require_once "customers.php";
require_once "constants.php";
class Api extends Rest {
public $dbConn;
public function __construct(){
parent::__construct();
$db = new DbConnect;
$this->dbConn = $db->connect();
}
public function create_insert_new_delivery(){
$shipping_fee= $this->validateParameter('item_category', $this->param['shipping_fee'], STRING, true);
try {
$cust = new Customersss;
} catch (Exception $e){
$this->throwError(ACCESS_TOKEN_ERRORS, $e->getMessage());
}
}
You are calling Rest::ThrowError() from Customersss class. It means your function is unreachable in this context.
To call this Rest method from the Customersss class, you can:
extend Rest to Customersss (see inheritance)
make ThrowError() a static method (see static)
Using inheritance:
class Customersss extends Rest { /* your properties/methods */ }
Using a static method:
class Rest {
static public function throwError($code, $message){ /* your code */ }
}
Callable with Rest::ThrowError()

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.

Call to Object method, executing a closure Property - Error

I have 2 classes, an event which holds the associated sessions the listener will check for and the method to execute and also a listener which monitors the sessions on page load and executes the corresponding event to that.
class Event
{
private $Sesh;
private $Method;
public function set(
$sesh = array(),
$method
) {
$this->Sesh = $sesh;
$this->Method = $method;
}
public function get(
) {
return [$this->Sesh,$this->Method];
}
}
class Listener
{
private $Sesh;
public function setSesh(
$foo
) {
$this->Sesh = $foo;
}
private $Event;
public function set(
$foo,
Event $event
) {
$this->Event[$foo] = $event;
}
public function dispatch(
$foo
) {
$state = true;
if(isset($this->Event[$foo]))
{
foreach($this->Event[$foo]->get()[0] as $sesh)
{
if(!isset($this->Sesh[$sesh]) || empty($this->Sesh[$sesh]))
{
$state = false;
}
}
}
// this line seems to be the cause at the method closure execution - not sure why?
return ($state) ? [true,$this->Event->get()[1]()] : [false,"Event was not triggered."];
}
}
When I run something like this:
$e = new Event;
$e->set(['misc'], function() { return 'Event method called.'; });
$l = new Listener;
$l->setSesh(array('misc' => 'example'));
$l->set('example', $e);
$l->dispatch('example');
I am getting this error:
FATAL ERROR Uncaught Error: Call to a member function get() on array in /home/phptester.net/public_html/code.php70(5) : eval()'d code:55 Stack trace: #0 /home/phptester.net/public_html/code.php70(5) : eval()'d code(65): Listener->dispatch('example') #1 /home/phptester.net/public_html/code.php70(5): eval() #2 {main} thrown on line number 55
Which seems to correspond to the method execution in the dispatch method of the Listener object, although I don't see why it is doing this? I am running PHP 7.0
My expected output in this case would be a returned array from the dispatch method containing: true and a string - 'Event method called.'
EDIT:
// returns array(1) { [0]=> string(4) "misc" }
var_dump($this->Event[$foo]->get()[0]);
// returns object(Closure)#2 (0) { }
var_dump($this->Event[$foo]->get()[1]);
// returns expected string output
var_dump($this->Event[$foo]->get()[1]());

Wrong Static Method

PHP calls private method in parent class instead of method define in current class called by call_user_func
class Car {
public function run() {
return call_user_func(array('Toyota','getName')); // should call toyota
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); //Car instead of Toyota
$toyota = new Toyota();
echo $toyota->run(); //Car instead of Toyota
I have found a solution with a different approach..
<?php
class Car {
public static function run() {
return static::getName();
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
echo Car::run();
echo Toyota::run();
?>
Using Late Static Binding..
You might use something like this:
<?php
class Car {
public function run() {
return static::getName();
}
private static function getName(){
return 'Car';
}
}
class Toyota extends Car {
public static function getName(){
return 'Toyota';
}
}
$car = new Car();
echo $car->run();
echo PHP_EOL;
$toyota = new Toyota();
echo $toyota->run();
?>
Output:
Car
Toyota
PHP 5.4.5
This is a bug that appears to have fluctuated in and out of existence for a long time (see #deceze's tests in comments on the question). It is possible to "fix" this issue - that is, give consistent behaviour across PHP versions - using reflection:
Works in PHP 5.3.2 and later due to a dependency on ReflectionMethod::setAccessible() to invoke private/protected methods. I will add further explanation for this code, what it can and can't do and how it works very shortly.
Unfortunately it's not possible to test this directly on 3v4l.org because the code is too large, however this is the first ever real use case for minifying PHP code - it does work on 3v4l if you do this, so feel free to play around and see if you can break it. The only issue I'm aware of is that it doesn't currently understand parent. It is also restricted by the lack of $this support in closures before 5.4, there's not really anything that can be done about this though.
<?php
function call_user_func_fixed()
{
$args = func_get_args();
$callable = array_shift($args);
return call_user_func_array_fixed($callable, $args);
}
function call_user_func_array_fixed($callable, $args)
{
$isStaticMethod = false;
$expr = '/^([a-z_\x7f-\xff][\w\x7f-\xff]*)::([a-z_\x7f-\xff][\w\x7f-\xff]*)$/i';
// Extract the callable normalized to an array if it looks like a method call
if (is_string($callable) && preg_match($expr, $callable, $matches)) {
$func = array($matches[1], $matches[2]);
} else if (is_array($callable)
&& count($callable) === 2
&& isset($callable[0], $callable[1])
&& (is_string($callable[0]) || is_object($callable[0]))
&& is_string($callable[1])) {
$func = $callable;
}
// If we're not interested in it use the regular mechanism
if (!isset($func)) {
return call_user_func_array($func, $args);
}
$backtrace = debug_backtrace(); // passing args here is fraught with complications for backwards compat :-(
if ($backtrace[1]['function'] === 'call_user_func_fixed') {
$called = 'call_user_func_fixed';
$contextKey = 2;
} else {
$called = 'call_user_func_array_fixed';
$contextKey = 1;
}
try {
// Get a reference to the target static method if possible
switch (true) {
case $func[0] === 'self':
case $func[0] === 'static':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of self:: in an invalid context');
}
$contextClass = new ReflectionClass($backtrace[$contextKey][$func[0] === 'self' ? 'class' : 'object']);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
case is_object($func[0]):
$contextClass = new ReflectionClass($func[0]);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if ($method->isStatic()) {
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
} else {
$invokeContext = $func[0];
}
break;
default:
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
$method = new ReflectionMethod($func[0], $func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if (empty($backtrace[$contextKey]['object'])
|| $func[0] !== $contextClass->getName()
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
if (empty($backtrace[$contextKey]['object']) || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method outside a class context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
}
// Invoke the method with the passed arguments and return the result
return $method->invokeArgs($invokeContext, $args);
} catch (Exception $e) {
trigger_error($called . '() expects parameter 1 to be a valid callback: ' . $e->getMessage(), E_USER_ERROR);
return null;
}
}
Use "protected" modifier if you want to get access from parent and descendants only. IMO, it's obvious. For example:
<?php
class Car {
public function run() {
return call_user_func(array('static','getName'));
}
protected static function getName() {
return 'Car';
}
}
class Toyota extends Car {
protected static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); // "Car"
$toyota = new Toyota();
echo $toyota->run(); // "Toyota"
You can use get_called_class() instead of 'static'.
The problem is, I think, with the different access levels of the two getname functions. If you make the base class version of getname() public (the same as the derived class version), then in php 5.3.15 (on my Mac), you get Toyota. I think that, because of the different access levels, you end up with two different versions of the getname() function in the Toyota class, rather than the derived class version overriding the base class version. In other words, you have overloading rather than overriding. Therefore, when the run() function looks for a getname() function in the Toyota class to execute, it finds two and takes the first one, which would be the first to be declared (from the base class).
Granted this is just supposition on my part, but it sounds plausible.
use the get_called_called function todo this
public function run() {
$self = get_called_class();
return $self::getName();
}
I believe you're functions are overriding each other and by default going to the first one. Unless you change the parameters of one function, or rename the function it will always default to the parent class function.

php typecast constructor

I would like to typecast PHP exceptions. Consider the following code:
class myException extends Exception {
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
The idea is to turn a standard Exception into a myException preserving the original stack trace. Since the variables holding the trace are private I cannot copy these values immediately and the CTOR produces a new one for myException.
The first idea was of course to use clone, but I can hardly re-assign $this, can I?
So what I'm trying to do is a C++ style typecast CTOR. Is there a sensible paradigm in PHP to do this?
Why not just set trace & previous the same way as file & line?
class myException extends Exception {
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
$this->trace = $mOrigin->getTrace();
$this->previous = $mOrigin->getPrevious();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
EDIT:
See comments below regarding why I got away w/ this code earlier.
Why not turn your myException class into a decorator:
class myException extends Exception {
private $_oException;
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
$this->_oException = $mOrigin;
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
function getTrace()
{
return $this->_oException->getTrace();
}
function getPrevious()
{
return $this->_oException->getPrevious();
}
}
FUTHER INFO:
I've followed up on php-general and it turns out this is the intended behavior and it works the same in Java et al as well. You can override the member variable in child classes and have a separate store with the same name. This compiles just fine in java
public class PrivateAccess
{
private Boolean isAccessible = true;
public Boolean getAccessible()
{
return isAccessible;
}
}
class PrivateAccessChild extends PrivateAccess
{
private Boolean isAccessible = false;
public Boolean getAccessible()
{
return isAccessible;
}
public Boolean getParentAccessible()
{
return super.getAccessible();
}
public static void main(String[] args)
{
PrivateAccessChild pAccess = new PrivateAccessChild();
if(!pAccess.getAccessible())
System.out.println("we're hitting the child here...");
if(pAccess.getParentAccessible())
System.out.println("we're hitting the parent here...");
System.out.println("we're done here...");
}
}

Categories