I have a property that stores a class name as a string. I then want to use this to call a static method of said class. As far as I know, this is possible since PHP 5.3. I am running 5.6.x on a vagrant box.
I want to do this:
$item = $this->className::getItem($id);
But I get the following error:
Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)...
The following works fine:
$c = $this->className;
$item = $c::getItem($id);
Any idea why? Is this not the same thing?
The problem is that you are access are property from a class in the first useage, but then in the second try you are parsing the value of the class property (into $c), what is a classname as string, and this can used for static calls to static class functions. The first try, trys to access the static method on an string (the class property).
class a {
static function b(){echo'works';}
}
$a='a';
$a::b();
But the real issue of the error is, that this ->FooBar:: is an syntax error in PHP.
JOUM is completely right!
Based on his answer I wrote a class like a fabric.
Interface GetItem
{
public static function getItem($id);
}
Abstract Class Item
{
private $id;
function __construct($id)
{
$this->id = $id;
}
}
Class ItemA extends Item implements GetItem
{
public static function getItem($id)
{
$item = new ItemA($id);
return $item;
}
}
Class ItemB extends Item implements GetItem
{
public static function getItem($id)
{
$item = new ItemB($id);
return $item;
}
}
Class Fabric
{
function fabricItem($classname,$id)
{
$item = $classname::getItem($id);
return $item;
}
}
$fabric = new Fabric();
$a = $fabric->fabricItem("ItemA",3);
$b = $fabric->fabricItem("ItemB",4);
var_dump($fabric);
var_dump($a);
var_dump($b);
Related
Parent Class
class admarvel_generic_network
{
protected $attributeSettings;
public function __construct()
{
$this->attributeSettings = "something";
}
public static function parentGetAd()
{
print_r($this->attributeSettings); //throws FATAL ERROR Using $this when not in object context
}
}
Child Class - Initiating object of same class within static function
class Agencies_selectablemedia_test extends admarvel_generic_network
{
public static function getAd($frengoAdParams)
{
$adnw = new Agencies_selectablemedia_test();
$ad = $adnw->parentGetAd();
return $ad;
}
}
//Entry point
$ad_contents = Agencies_selectablemedia_test::getAd($params);
echo $ad_contents;
I get a fatal error, as highlighted in the code above.
I checked that if I make the following changes in child and parent class -
Parent Class
public static function parentGetAd($obj)
{
print_r($obj->attributeSettings); //this works
}
Child Class
public static function getAd($frengoAdParams)
{
$adnw = new Agencies_selectablemedia_test();
$ad = admarvel_generic_network::parentGetAd($adnw); //used scope resolution operator and passed object as parameter.
return $ad;
}
Could someone explain the above? I would like to understand why I cannot use $this->attributeSettings in the parent class's parentGetAd() function.
The reason why you cannot access $this->attributeSettings is that your are in a static method. So you aren't in the context of an object.
public static function parentGetAd($obj)
If you are declaring the method like this
public function parentGetAd($obj) {
}
you should be able to access $this->attributeSettings.
I have two classes. Class B has field: object of class A (composition relationship). It is necessary to get static variable of class A. But there are some problems in code:
<?php
class A {
public static $var = 'a';
}
class B {
private $object;
private function staticAccess($className) {
$this->object = $className;
}
public function __construct() {
$this->staticAccess('A');
// This is wrong syntax:
//$a = $this->object::$var;
// Syntax which works but unconvenient
$objA = $this->object;
$a = $objA::$var;
}
}
As you saw there is a solution. But it is necessary to write additional line. Is it possible to solve a task in one line?
Thanks you for any help!
It's not possible to do in one line (just a constraint of PHP). I'd suggest adding a function that you can use, something like this:
public function getStaticVar($var) {
$class = new ReflectionClass($this->object);
$value = $class->getStaticPropertyValue($var);
return $value;
}
Using the Reflection library is the only way of dynamically accessing a dynamic static property in PHP.
I want to know if I can somehow assign new variable without making constructor.
It seems pretty big overkill to create constructors on every class just to set initial private class variables.
Here is my example of what I want to achieve
<?php
class MyClass {
public function DoSomething() {
echo '1';
}
}
class MySecondClass {
private $obj = new MyClass(); // Error
/*
// This works, but I don't like it, I think it's total overkill
function __construct() {
$this->obj = new MyClass();
}
*/
public function PrintOne() {
$this->obj->DoSomething();
}
}
$class = new MySecondClass();
$class->PrintOne();
Just so it's perfectly clear here's the error message
syntax error, unexpected 'new' (T_NEW) on line 10
You can't (that I know of), you need to either instantiate it in the constructor (Option A), or pass in the object (Option B).
Option A:
class MySecondClass {
private $obj;
function __construct() {
$this->obj = new MyClass();
}
public function PrintOne() {
$this->obj->DoSomething();
}
}
Option B:
class MySecondClass {
private $obj;
function __construct(MyClass $obj) {
$this->obj = $obj;
}
public function PrintOne() {
$this->obj->DoSomething();
}
}
You can't do that in that manner. You can have properties be initialized but they need to be a constant value and not the result of a function or trying to instantiate a class.
http://www.php.net/manual/en/language.oop5.properties.php
IMO, You shouldn't instantiate new objects within the constructor of your classes. You should pass them in as arguments for creating the object.
i have a model class which extends CComponent
class CompanyModel extends CComponent{
private $company_pk;
public function getCompany_pk()
{
return $this->company_pk;
}
public function setCompany_pk($value) {
$this->company_pk = $value;
}
}
I have a function which fills this modal
public function getCompanyList() {
$companyList=array();
$company_obj = new CompanyModel();
$sql = "SELECT company_pk,name FROM tbl_company WHERE status = ".Constants::ACTIVE_COMPANY;
$command=$this->connection->createCommand($sql);
$command->setFetchMode(PDO::FETCH_ASSOC);
$rows=$command->queryAll();
foreach ($rows as $row){
$company_obj->company_pk = $row['company_pk'];
array_push($companyList,$company_obj);
}
return $companyList;
}
and my controller
Class UserController extends CController {
public function actionGetCompanyList() {
$model = new UserAction();
$ret_val = $model->getCompanyList();
echo CJSON::encode((array)$ret_val[0]);
}
}
and the JSON i get is
{"\u0000CompanyModel\u0000company_pk":"2"}
How can i remove those garbage values
I asume you can't decode them later, if you would change $company_pk to public it should encode correctly. Problem is with your object cast to array which adds NULL byte to your private member $company_pk. In echo CJSON::encode((array)$ret_val[0]); as it's array of OBJECTS.
You can do nasty.
$json = '{"\u0000CompanyModel\u0000company_pk":"2"}';
$json = str_replace('\u0000', '', $json);
var_dump(json_decode($json));
Or you pass object to CJSON::encode i dont know much of YII but it should handle object, as per manual http://www.yiiframework.com/doc/api/1.1/CJSON.
Here is example to reproduce issue:
class test {
private $private = 1;
public $publiv = 2;
}
$obj = new test();
$array = (array) $obj;
$json = json_encode($array);
var_dump($json);
var_dump(json_decode($json));
EDIT:
From the manual http://www.php.net/manual/en/language.types.array.php:
If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible; private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name. These prepended values have null bytes on either side.
I did quick look to CJSON::encode from YII and you can use object directly, but your object must be traversable, so you must implement Iterator interface. http://www.php.net/manual/en/class.iterator.php.
EDIT:2
Implementing interface could be tricky, there is another option to call get_object_vars from within object and in this case you will get array which will work.
class CompanyModel extends CComponent
{
private $company_pk;
public function getCompany_pk()
{
return $this->company_pk;
}
public function setCompany_pk($value)
{
$this->company_pk = $value;
}
public function export()
{
return get_object_vars($this);
}
}
And then:
$ret_val = $model->getCompanyList();
echo CJSON::encode($ret_val->export());
Problem why it does not work for you with normal object because YII uses get_object_vars internally and it can't access private properties when it's in different scope.
I found out that if i extend CModel instead of CComponent in the model class, i could achieve what exactly i wanted. While extending this class you will have to override one abstract method. Otherwise it will throw error. My new Model class look like this
class CompanyModel extends CModel{
private $company_pk;
public function attributeNames()
{
return array( 'company_pk' );
}
public function attributeLabels()
{
return array( 'company_pk' => 'company_pk Label' );
}
public function getCompany_pk()
{
return $this->company_pk;
}
public function setCompany_pk($value) {
$this->company_pk = $value;
}
}
class Fruit {
protected $blend;
public function WillItBlend() {
return $this->blend;
}
public static function MakeFruit() {
$objF = new Fruit();
$objF->blend = true;
return $objF;
}
}
$fruit = Fruit::MakeFruit();
echo $fruit->WillItBlend();
Why is this line working $objF->blend = true; instead of throwing a Fatal error ?
The visibility modifiers work at the class level, not at the object level. This also means that objects of the same class can access each other's private bits.
An example at the PHP interactive prompt:
php > class Foo {
private $bar;
public function __construct() { $this->bar = rand(1, 100); }
public function baz($another_foo) { echo $another_foo->bar, '-', $this->bar; }
}
php > $a = new Foo();
php > $b = new Foo();
php > $a->baz($b);
86-70
$objF is instance of class Fruit.
$objF->blend is being used in class itself. Protected properties can be used in class itself.
You will get Fatal Error if you use it outside the class as $fruit->blend;
So it is allowed to do so.
because you're accessing it from inside the class, if you would call from outside the class
$fruit = Fruit::MakeFruit();
echo $fruit->WillItBlend();
echo $fruit->blend;
it would throw a fatal error.