There are many methods around that allow you to access private variables of another class, however what is the most efficient way?
For example:
I have this class with some details in:
class something{
private $details =
['password' => 'stackoverflow',];
}
In another class I need to access them, example (although this obviously wouldn't work since the variable isn't in scope to this class):
class accessSomething{
public function getSomething(){
print($something->details['password']);
}
}
Would a function like this be good enough within the class "something" to be used by the access class?:
public function getSomething($x, $y){
print $this->$x['$y'];
}
you should be using proper getters/setters in your classes to allow access to otherwise restricted data.
for example
A class
class AClass {
private $member_1;
private $member_2;
/**
* #return mixed
*/
public function getMember1() {
return $this->member_1;
}
/**
* #param mixed $member_1
*/
public function setMember1( $member_1 ) {
$this->member_1 = $member_1;
}
/**
* #return mixed
*/
public function getMember2() {
return $this->member_2;
}
/**
* #param mixed $member_2
*/
public function setMember2( $member_2 ) {
$this->member_2 = $member_2;
}
}
which is then called as follows:
$newClass = new AClass();
echo $newClass->getMember1();
echo $newClass->getMember2();
Related
This question already has answers here:
PHP class instance to JSON
(5 answers)
Closed 4 years ago.
I got an object. I need to turn into JSON for storage but when I try to encode it into JSON it returns an empty JSON object. When I tried to use json_last_error.
The code I used
echo $payload["sub"];
echo json_encode($user);
echo json_last_error_msg();
The result I get
"102573480781696194937{}No error".
The User class I'm trying to encode
<?php
/**
* Created by PhpStorm.
* User: Student
* Date: 13-4-2018
* Time: 10:40
*/
namespace php;
class User
{
private $isAdmin = false;
private $registeredFood = array();
private $googleID;
private $name;
private $notes = array();
private $email;
/**
* User constructor.
* #param $googleID
*/
public function __construct($googleID)
{
$this->googleID = $googleID;
}
/**
* #return mixed
*/
public function getGoogleID()
{
return $this->googleID;
}
/**
* #return bool
*/
public function isAdmin()
{
return $this->isAdmin;
}
/**
* #param bool $isAdmin
*/
public function setIsAdmin($isAdmin)
{
$this->isAdmin = $isAdmin;
}
/**
* #return array
*/
public function getRegisteredFood()
{
return $this->registeredFood;
}
/**
* #param array $registeredFood
*/
public function setRegisteredFood($registeredFood)
{
$this->registeredFood = $registeredFood;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return array
*/
public function getNotes()
{
return $this->notes;
}
/**
* #param array $notes
*/
public function setNotes($notes)
{
$this->notes = $notes;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #param mixed $email
*/
public function setEmail($email)
{
$this->email = $email;
}
}
?>
I hope someone can help me
It is because your class's properties are private.
An example class with only private properties ...
php > class Foo { private $bar = 42; }
php > $obj = new Foo();
do not expose values:
php > echo json_encode($obj);
{}
But an example class with public properties ...
php > class Bar { public $foo = 42; }
php > $objBar = new Bar();
do it!
php > echo json_encode($objBar);
{"foo":42}
\JsonSerializable
PHP provide an'interafce \JsonSerializable that require a method jsonSerialize. This method is automatically called by json_encode().
class JsonClass implements JsonSerialize {
private $bar;
public function __construct($bar) {
$this->bar = $bar;
}
public function jsonSerialize() {
return [
'foo' => $this->bar,
];
}
}
I prefer this solution because is not good to expose publicly properties
serialization and unserialization ...
If you need to serialize and unserialize php object you can ...
php > class Classe { public $pub = "bar"; }
php > $obj = new Classe();
php > $serialized = serialize($obj);
php > $original = unserialize($serialized);
php > var_dump($original);
php shell code:1:
class Classe#2 (1) {
public $pub =>
string(3) "bar"
}
$serialized variable contains O:6:"Classe":1:{s:3:"pub";s:3:"bar";}. As you can see is not a json, but is a format that allow you to recreate original object using unserialize function.
You have a couple of options here.
Option 1: Make your class properties public
Like what sensorario mentioned, change the visibility of your properties so that it is accessible from outside the class, which is where you are calling json_encode.
Option 2: Introduce a method/function within the class to return the encoded JSON object
Have a toJson() function inside your User class.
Of course, there are way more options - such as extending User so that User is not "contaminated", etc.
But yup, the general problem is your private properties.
In our application, we use repositories for models that are fetched from the database. So, we have an abstract repository that knows about the database, has a loadById method to load a database record and an abstract getEntity method that creates an object for that specific repository. Example code:
abstract class EntityRepository {
/**
* #param int $id
* #return AbstractEntity
*/
public function loadById($id) {
$record = $this->db->loadById($id);
$entity = $this->getEntity();
return $this->inflate($record, $entity);
}
/**
* #return AbstractEntity
*/
protected abstract function getEntity();
}
class PeopleRepository extends EntityRepository {
protected function getEntity() {
return new PeopleEntity();
}
}
abstract class AbstractEntity {
private $id;
/**
* #return int
*/
public function getId() {
return $this->id;
}
/**
* #param int $id;
*/
public function setId($id) {
$this->id = $id;
}
}
class PeopleEntity extends AbstractEntity {
private $name;
/**
* #return string
*/
public function getName() {
return $this->name;
}
/**
* #param string $name;
*/
public function setName($name) {
$this->name= $name;
}
}
When using an instance of PeopleRepository and fetching a model through loadById, PhpStorm is not able to resolve the returned model to a concrete type, but provides only code completion for the functions of AbstractEntity. Is there any simple way to make it work?
In https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata, I've only found ways to make it work for concrete classes and their functions. So, enumerating all repository classes and all their ways of creating an entity might work. But I'd love to see an abstract way of defining like "All instances of EntityRepository will return an entity of that type defined in getEntity() when loading an entity"
I doubt there's a blanket way of doing this. Even using PHPStorm meta you have to be explicit for each case. Perhaps the way of doing this is by doing something like adding a repository facade e.g.
class RepositoryFacade {
public static function __callStatic($method, $args) {
if ($args[0] == People::class) {
array_shift($args);
return new PeopleRepository()->{$method}(...$args);
}
}
}
Then you might be able to typehint this using:
override(RepositoryFacade::loadById(0), type(0));
Of course the facade is not really the best pattern to be using in general so I can see how this might not be ideal.
my problem is getting the right type of object from a method, which is returning a "mixed" type due to inhreitance.
I've got a generic list class
class List
{
/**
* #var Item[]
*/
protected $items;
public function __construct()
{
$this->items = array();
}
/**
* #return Item[]
*/
public function getAll()
{
return $this->items;
}
/**
* #return Item
*/
public function getOne($index)
{
if (isset($this->items[$index])) {
return $this->items[$index];
}
return null;
}
}
containing element of type Item, which is a generic class either
class Item
{
/**
* #var string
*/
protected $name;
public function __construct($name)
{
$this->name = $name;
}
}
Such generic classes are extended by N different lists. Just an example
class UserList extends List
{
/* user-specific implementation */
}
class User extends Item
{
/* user-specific implementation */
}
In the client code
$user_list = new UserList();
foreach ($user_list->getAll() as $user) {
echo $user->getEmailAddr();
}
Inside the foreach I don't have code completion, because my getAll method (inherited from the father) is returning Item[], or mixed[], not a User[]. Same problem with getOne method.
I wouldn't like to have to override such methods.
Is there a more clever and elegant solution?
Thank you
I don't think there's any way for the IDE to infer the type automatically. Use a phpdoc type annotation:
foreach ($user_list->getAll() as $user) {
/** #var User $user */
echo $user->getEmailAddr();
}
See the related question PHPDoc type hinting for array of objects?
I have a class that controls an external API that I use in several projects (simplified example):
class PaymentModule
{
public function doPayment($customer_name, $currency, $language)
{
curl_setopt(POSTFIELDS, array($customer_name, $currence, $language));
curl_exec();
}
}
Now in a specific project I would like to "wrap" it and provide sensible defaults for a lot of parameters that I don't use here.
So I thought, I will just extend this class, have my IDE override all methods and then remove the parameters that I don't use, like this:
class MyPaymentModule extends PaymentModule
{
public function doPayment($customer_name)
{
$language = get_current_language();
parent::doPayment($customer_name, 'EUR', $language);
}
}
As I now learned (thanks to PHP strict standards), this violates the Liskov substitution principle, i.e. in OOP in general MyPaymentModule is expected to have the same interface as PaymentModule which in turn means MyPaymentModule::doPayment() is expected to have the same parameters as PaymentModule::doPayment().
I think it's not too uncommon that you want to create a class that provides sensible defaults to another one, so is there any common pattern to use here?
Of course I could go for two completely independent classes, but I would prefer a solution that still hints at the relationship between the two classes... after all they will always have the same methods, just one with less parameters.
Thats not possible with your current class design.
I would suggest to create a new class PaymentOptions which handles currency and language.
Passing the options to doPayment either with a new method setPaymentOptions or as an optional parameter for doPayment.
Something like this should work
class PaymentModule {
/**
* #var PaymentOptions
*/
private $paymentOptions = null;
function doPayment($customer_name, PaymentOptions $options = null) {
if ($options == null) {
$options = $this->paymentOptions;
}
if ($options == null) {
throw new Exception('...');
}
//...
}
/**
* #return PaymentOptions
*/
public function getPaymentOptions()
{
return $this->paymentOptions;
}
/**
* #param PaymentOptions $paymentOptions
*/
public function setPaymentOptions($paymentOptions)
{
$this->paymentOptions = $paymentOptions;
}
}
class MyPaymentModule extends PaymentModule {
function doPayment($customer_name, PaymentOptions $options = null)
{
//...
}
}
class PaymentOptions {
private $currency;
private $language;
/**
* #return mixed
*/
public function getCurrency()
{
return $this->currency;
}
/**
* #param mixed $currency
*/
public function setCurrency($currency)
{
$this->currency = $currency;
}
/**
* #return mixed
*/
public function getLanguage()
{
return $this->language;
}
/**
* #param mixed $language
*/
public function setLanguage($language)
{
$this->language = $language;
}
}
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!