The right way to set a protected variable from another class PHP - php

I have this issue to solve:
I create two classes where the second is an extends of the first and there I want to
set and get an variable from the first class, but... I can't find the 'right' way to do it
basically this:
class class_one {
protected $value;
private $obj_two;
public function __construct() {
$this->obj_two = new class_two;
}
public function firstFunction() {
$this->obj_two->obj_two_function();
echo $this->value; // returns 'New Value' like set in the class two
}
}
class class_two extends one {
public function obj_two_function() {
"class_one"->value = 'New Value';
}
}
How can I do this?

First class should not initialize the second, unless you're seeking Uroboros! Protected variables can be set by an extended class without any function support. Just go $this->protVariable = "stuff";
But you will need a function that may be protected to set ClassOne's private variable from the second class. Likewise a function must be made in ClassOne to actually retrieve its value.
class ClassOne {
private $privVariable;
protected $protVariable;
/**
*/
function __construct () {
}
/**
* This function is called so that we may set the variable from an extended
* class
*/
protected function setPrivVariable ($privVariable) {
$this->privVariable = $privVariable;
}
}
In the second class you can then call to parent::setPrivVariable() to set the value using the parent function.
class ClassTwo extends \ClassOne {
/**
*/
public function __construct () {
parent::__construct ();
}
/**
* Set the protected variable
*/
function setProtVariable () {
$this->protVariable = "stuff";
}
/**
* see ClassOne::setPrivVariable()
*/
public function setPrivVariable ($privVariable) {
parent::setPrivVariable ( $privVariable );
}
}

Related

Too few arguments to function App\Exports\NilaiExport::__construct(), 0 passed in NilaiController.php on line 112 and exactly 1 expected

I want to export data to excel from $id, but an error occurred when I added the constructor in App/Export/NilaiExport. thanks
NilaiController.php
function download($id){
return Excel::download(new NilaiExport, 'Nilai.xlsx');
}
NilaiExport.php
<?php
namespace App\Exports;
use App\Khs;
use Maatwebsite\Excel\Concerns\FromCollection;
class NilaiExport implements FromCollection
{
/**
* #return \Illuminate\Support\Collection
*/
protected $id;
public function __construct($id) {
$this->id = $id;
}
public function collection()
{
return Khs::findOrFail($this->id);
}
}
function download($id){
return Excel::download(new NilaiExport($id), 'Nilai.xlsx');
}
You need to pass through the id variable to the constructor method, it does say in the error message that it expects 1 parameter but you didn't supply one.
If the parameter is optional then the constructor signature should look something like this
public function __construct($id = null) {
$this->id = $id;
}
and you would define id as
/**
* #var \Illuminate\Support\Collection|null
*/
protected $id;
You have used the constructor inside the NilaiExport class, but you do not pass the parameter class when creating the object, you can use the following code.
use blew
public $id;
public function __construct()
{
$this->id = app()->make($id);
}
instead of this
public function __construct($id) {
$this->id = $id;
}

Is it bad practice to include variable which must be initialised by subclass constructor in PHP?

I've been doing a lot of reading on constructors and initialising variables, and I've came across a problem which I'm trying to solve. I'm trying to solve the lack of generics support by introducing a variable that needs to be initialised by the subclass.
<?php
abstract class Collection_Base {
protected $typeOf; // Must be initialised by the subclass
protected $collection = array();
}
class Cookie_Collection extends Collection_Base {
protected $typeOf = 'System\Web\Http_Cookie';
public function set ($item) {
if (!$item instanceof $this->typeOf) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}
}
?>
So I was wondering, is it bad practice to include variable which must be initialised by subclass constructor in PHP? Is there anything I need to be aware of when doing so?
While not directly related, I've used the following sources to gather my information:
http://docs.hhvm.com/manual/en/hack.otherrulesandfeatures.classinitialization.php
http://ralphschindler.com/2012/03/09/php-constructor-best-practices-and-the-prototype-pattern
SOLUTION
<?php
abstract class Collection_Base {
protected $collection = array();
public abstract function getType();
private function getTypeInternal () {
$type = $this->getType();
if (is_class($type)) {
throw new \UnexpectedValueException;
}
return $type;
}
public function set ($item) {
$type = $this->getTypeInternal();
if (!$item instanceof $type) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}
}
class Cookie_Collection extends Collection_Base {
protected $type = 'System\Web\Http_Cookie';
public function getType () {
return $this->type;
}
}
?>
I thought I recognized this as an anti-pattern, so I looked for where I read about it, but then I remembered it was this: http://en.wikipedia.org/wiki/Call_super, which isn't quite the same thing.
On to what you are doing however. There are a lot of similar libraries which use a practice like this, however they differ by enforcing the practice in way of abstract methods:
abstract class Collection_Base {
protected $typeOf;
protected $collection = array();
/**
* #return string
*/
public function getType()
{
if (null === $this->typeOf) {
$this->typeOf = $this->doGetType();
}
return $this->typeOf;
}
/**
* #return string
*/
abstract protected function doGetType();
}
class Cookie_Collection extends Collection_Base {
/**
* #param $item
*/
public function set ($item) {
if (!$item instanceof $this->getType()) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}
/**
* #inheritdoc
*/
protected function doGetType()
{
return 'System\Configuration\Configuration_Element';
}
}
Use a Factory pattern to hide constructor details ... see http://www.phptherightway.com/pages/Design-Patterns.html
Make Collection_Base an abstract class and define a method that returns appropriate class name:
abstract class Collection_Base
{
protected $collection = [];
public function add($item)
{
if (!$item instanceof $this->getType()) {
throw new \InvalidArgumentException();
}
$this->collection[] = $item;
}
abstract protected function getType();
}
class Collection_Cookie extends Collection_Base
{
protected function getType()
{
return Configuration_Element::class;
}
}
Using this approach it's not possible for other developers to forget about type "property".
EDIT:
Using a factory, as suggested by Luca Rocchi is also a very good idea.

Code completion in injected classes

Here is my sample code which is what I want to get, but in User class I get no codecompletion (for $this->app) in PHPSotrm.
How to change that code to enable code completion? I want to avoid global.
namespace myApp
class app
{
public $pdo = "my PDO";
public function __construct() {
$this->user=new User($this);
$this->test=new Test($this);
echo $this->user->getPDO();
//"my PDO"
echo $this->test->getUserPDO();
//"my PDO"
}
}
class User
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getPDO() {
return $this->app->pdo;
//no code completion
}
}
class Test
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getUserPDO() {
return $this->app->user->getPDO;
//no code completion
}
}
There's plenty of information on how to achieve this. All you need to do is to add the PHPDoc describing the property type.
class User
{
/**
* #var App
*/
private $app=null;
/**
* #param App $app
*/
public function __construct($app) {
$this->app = $app;
}
/**
* #return PDO
*/
public function getPDO() {
return $this->app->pdo;
}
}
If properties are implemented via magic getters / setters, you can use the same principles on the class itself.
/**
* #property App $app
* #method Pdo getPdo()
*/
class User
{
// …

How to define a static property in PHP?

Here is my source:
class Test {
public static function a() {
$share_var = ClassConfig::getVarA(); // return a hardcoding const 10;
// echo $share_var;
// ...
}
}
public static function b() {
$share_var = ClassConfig::getVarA(); // return a hardcoding const 10;
// echo $share_var;
// ...
}
}
}
So $share_var = ClassConfig::getVarA(); is called twice. So I do something like this:
class Test {
private static $share_var = ClassConfig::getVarA(); // return a hardcoding const 10;
public static function a() {
// echo $share_var;
// ...
}
}
public static function b() {
// echo $share_var;
// ...
}
}
}
But it failed.
How can I do that.
You can define the property as static, but it's no option for you because you can't call a method during class variable definition
class TestA {
private static $share_var_private = 10; // You can do this, but you can't call another method here eg TestB::a();
private static $share_var_private = Config::getA(); // This won't work
public static function a() {
echo self::$share_var_private;
}
}
If you want static methods, then you need something like own initialize method which will initialize the property but it has its disadvantages.
/**
* This is example of what I've described, but it is not so good for usage because you have to call init() method before you can use the class. You could call init method in each class method, but you can imagine it wouldn't be nice.
*/
class TestWithInit {
/**
* When it's defined as static, it can't be defined as private in php
*/
private static $share_var_static; // You can't call another class here eg TestB::a();
public static function init() {
self::$share_var_static = Config::getVarA();
}
public static function a() {
echo self::$share_var_static;
}
public static function b() {
echo self::$share_var_privat; // This would also work ... calling private variable as static (::)
}
}
Better option is probably singleton pattern which is instance of class but just once and is some ways it's quite close to static methods(NOT SAME).
class TestSingleton {
/**
* Singleton instance
*
* #var TestSingleton
*/
private $instance = null;
/**
* My special config value
*/
private $share_var;
/**
* For singleton make construct private
*/
private function __construct() {
// You can now initialize this private variable in constructor
self::$share_var = Config::getVarA();
}
/**
* Returns instance of this class
*
* #return TestSingleton
*/
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self(); // also new TestSingleton() woudl work
}
// return the instance
return self::$instance;
}
/**
* See this method is not static anymore
*/
public function a() {
echo $this->share_var_static;
}
/**
* See this method is not static anymore
*/
public function b() {
echo $this->share_var_static;
}
}
// Then you would call the method as:
TestSingleton::getInstance()->a();
TestSingleton::getInstance()->b();
// or
$myInstance = TestSingleton::getInstance();
$myInstance->a();
$myInstance->b();
Next option is to use normal non-static methods and object instance, and initialization of the object property in constructor, but I guess you know how.
I assume you wanted something more like static...
You must use self:
class Test {
private static $share_var = 'Something';
public static function a() {
echo self::$share_var;
}
public static function b() {
echo self::$share_var;
}
}
Test::a();
Test::b();
class Test {
private $share_var = ClassConfig::getVarA();
public static function a() {
echo $this->share_var;
}
public static function b() {
echo $this->share_var;
}
}
if you want to learn more about static Keyword, you can click the followed link, it's detail: static keyword

PHP classes and functions/ undefined variable

Why do I get undefined property Takeover::user2 on function takeover?
I'm not sure what I'm doing wrong. Can someone help?
I can call user2->addsaldo() on main file but I can't call it inside another function. Why?
Class user
class User {
/**
* #AttributeType int
*/
private $iduser;
/**
* #AttributeType float
*/
private $saldo=0;
/**
* #AssociationType Portefolio
* #AssociationKind Composition
*/
public $idportefolio;
public function __construct($iduser){
$this->iduser = $iduser;
}
/**
* #access public
*/
public function getid() {
// Not yet implemented
}
/**
* #access public
*/
public function addsaldo($saldo) {
$this->saldo = $saldo;
}
}
Class takeover
class Takeover {
/**
* #AttributeType int
*/
private $idTakeover;
/**
* #AssociationType root
* #AssociationMultiplicity 1
*/
public $Root;
public $IdeasTakerover=array();
public function __construct($idTakeover){
$this->idTakeover = $idTakeover;
}
/**
* #access public
*/
public function GetIdCompraRoot() {
// Not yet implemented
}
public function AddIdeasTakeover($idTakeover, $idideia) {
$this->idTakeover = $idTakeover;
$this->idideia = $idideia;
array_push($this->IdeasTakerover,$idideia);
}
/**
* #access public
*/
public function Takeover() {
$this->user2->addsaldo(200); //USER2 DOES EXIST
}
}
Creating users and calling them:
$takeover = new Takeover(1);
for ($i=0; $i<$conta; $i++ ){
$takeover->AddIdeasTakeover(1,$idsideias[$i]);
}
$takeover->Takeover();
if ($partial == "user") {
$booleanUser = TRUE;
$iduser=substr($buffer, 4);
${'user'.$iduser} = new User($iduser);
}
The problem is not in this class. The problem is that $GLOBALS['user2'] is not defined when referenced here: $GLOBALS['user'.$this->IdeiasTakeover[$i]]. You then call addsaldo() on an undefined array element in $GLOBALS.
On another note, it is impossible to write good code using $GLOBALS. Global variables are bad news. You should not be using/referencing global variables. The exception would be at a low level when referencing $_POST, $_GET, etc. Even still, all the good PHP frameworks wrap these in a request object.
Edit
Dependency Injection is a better way for one class to use another:
class X {
$yInstance;
public function __construct($yInstance)
{
$this->yInstance = $yInstance;
}
public function x()
{
//Call your 'y' method on an instance of Y
$this->yInstance->y();
}
}
class Y {
public function y()
{
echo 'Y::y() called!';
}
}
Call X::x()
$y = new Y();
$x = new X($y);
$x->x();
Output:
Y::y() called!
The easiest way to do this is to use a dependency injection container. Here is my favorite one for PHP: http://symfony.com/doc/current/components/dependency_injection/introduction.html
Also, check out Martin Fowlers classic article about IOC:
http://martinfowler.com/articles/injection.html
Good luck!

Categories