I need to initialize some properties in my class. I don't use constructor, only set methods.
<?php
namespace Task;
class Log {
private $_views = null;
private $_urls = null;
private $_traffic = null;
/**
* #param int $count
*/
public function setViewCount(int $count) {
$this->$_views = $count;
}
/**
* #return int
*/
public function getViewCount() {
return $this->$_views;
}
/**
* #param int $count
*/
public function setUrlCount(int $count) {
$this->$_urls = $count;
}
/**
* #return int
*/
public function getUrlCount() {
return $this->$_urls;
}
/**
* #param int $trafficData
*/
public function setTraffic(int $trafficData) {
$this->$_traffic = $trafficData;
}
/**
* #return int
*/
public function getTraffic() {
return $this->$_traffic;
}
}
?>
Then I try to set values to properties and save it to associative array.
<?php
require 'Log.php';
use Task;
$log = new Task\Log();
$log->setViewCount(44);
$log->setUrlCount(55);
$log->setTraffic(99999);
$res = array("views" => $log->getViewCount(), "urls" => $log->getUrlCount(), "traffic" => $log->getTraffic());
echo json_encode($res);
?>
After encoding to json I see that any element of array has last value I set to object. In this example last is 99999 for Traffic so I got {"views":99999,"urls":99999,"traffic":99999}. What's the reason of such behaviour and how can I get correct values in each element of array?
$this->$_views this accesses not the field named _views but a field with the name stored in variable $_views.
Since you have no such variable the name assumed empty, thus the same name for each of setters or getters.
In short: you need to remove $ after ->:
$this->_urls = $count;
etc.
Related
I'm trying to create a list of unique objects in PHP.
I want to access each object by a unique name but I also need the object to know its name.
My idea was to create an associative array of objects like this:
class MyObject()
{
public $name;
public $property1;
public $property2;
function __construct($name)
{
$this->name = $name;
}
function doSomething()
{
echo $name.$property1;
}
function doSomethingElse()
{
echo $name.$property2;
}
}
$array = array();
$name = 'example';
$array[$name] = new MyObject($name);
$array[$name]->property1 = 'xyz';
$array[$name]->property2 = 123;
$name = 'test';
$array[$name] = new MyObject($uniqueName);
$array[$name]->property1 = 'abc';
$array[$name]->property2 = 321;
$array['example']->doSomething();
$array['test']->doSomethingElse();
I'm pretty new to PHP and coding in general and I feel like this is a really stupid solution so I wanted to ask if you know any better ways of doing this.
For a simple use-case you've provided, perhaps implementing the predefined interface ArrayAccess may fit the bill. Something like:
class UniqueCollection implements ArrayAccess
{
/**
* #var array<string, MyObject>
*/
private $collection = [];
/**
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset): bool
{
return isset($this->collection[$offset]);
}
/**
* #param mixed $offset
*
* #return MyObject|null
*/
public function offsetGet($offset): mixed
{
return $this->collection[$offset] ?? null;
}
/**
* #param mixed $offset
* #param array<mixed, mixed> $values
*
* #return void
*/
public function offsetSet($offset, $values): void
{
if ($offset === null) {
// Error
}
// Uncomment if we don't want to overwrite existing MyObject with the same name,
// eg, throw exception
// if (isset($this->collection[$offset]) {
// // Error
// }
$count($values);
if ($count !== 0 || $count !== 2) {
// Error
}
$myObj = new MyObject($offset);
if ($count) {
// Fetch the values only so we only have to deal with zero-based integer offsets
$values = array_values($values);
// Establish a convention on which index goes to what property
$myObj->property1 = $values[0];
$myObj->property2 = $values[1];
}
$this->collection[$offset] = $myObj;
}
/**
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset): void
{
return unset($this->collection[$offset]);
}
}
Use:
$set = new UniqueCollection();
$set['example'] = ['xyz', 123];
$set['test'] = []; // not exactly elegent, but hey ¯\_(ツ)_/¯
$set['test']->property1 = 'abc'; // should work fine, unless we clone the MyObject internally
$set['test']->property2 = 321; // ditto
$set['example']->doSomething();
$set['test']->doSomethingElse();
I haven't tested this so let me know if there are any issues.
I have the following class
namespace PG\Referrer\Single\Post;
class Referrer implements ReferrerInterface
{
/**
* #var $authorReferrer = null
*/
protected $isAuthorReferrer = null;
/**
* #var $dateReferrer = null
*/
protected $isDateReferrer = null;
/**
* #var $searchReferrer = null
*/
protected $isSearchReferrer = null;
/**
* #var $taxReferrer = null
*/
protected $isTaxReferrer = null;
/**
* #param array $values = null;
*/
public function __construct(array $values = null)
{
if ($values)
$this->setBulk($values);
}
/**
* Bulk setter Let you set the variables via array or object
*/
public function setBulk($values)
{
if (!is_array($values) && !$values instanceof \stdClass) {
throw new \InvalidArgumentException(
sprintf(
'%s needs either an array, or an instance of \\stdClass to be passed, instead saw %s',
__METHOD__,
is_object($values) ? get_class($values) : gettype($values)
)
);
}
foreach ($values as $name => $value) {//create setter from $name
global $wp_query;
if (array_key_exists($value, $wp_query->query_vars)) { //Check that user don't set a reserved query vars
throw new \InvalidArgumentException(
sprintf(
'%s is a reserved query_vars and cannot be used. Please use a unique value',
$value
)
);
}
$setter = 'set' . $name;
$condition = isset($_GET[$value]);
if ($setter !== 'setBulk' && method_exists($this, $setter)) {
$this->{$setter}($condition);//set value (bool)
}
}
return $this;
}
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer)
{
$this->isAuthorReferrer = $isAuthorReferrer;
return $this;
}
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer)
{
$this->isDateReferrer = $isDateReferrer;
return $this;
}
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer)
{
$this->isSearchReferrer = $isSearchReferrer;
return $this;
}
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer)
{
$this->isTaxReferrer = $isTaxReferrer;
return $this;
}
}
with its interface
namespace PG\Referrer\Single\Post;
interface ReferrerInterface
{
/**
* #param array $values
* #return $this
*/
public function setBulk($values);
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer);
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer);
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer);
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer);
}
This class sets up 4 conditionals that I need to use in another class. The values that is used in this class is also set from the other class, so basically the user sets values in the other class (lets call it class b) that is then used by class Referrer and returns the 4 conditionals which is then used by class b.
The reason why I'm doing it this way is because there will be two other classes that will need to do the same, but will returns different info
What is the more correct way to achieve this?
EDIT
To clear this up
class Referrer
The properties $isAuthorReferrer, $isDateReferreretc will either have a value of null or a boolean value depending on what is set by the user.
Example:
$q = new Referrer(['authorReferrer' => 'aq']);
In the code above, $isAuthorReferrer is set via the setBulk() method in the class to true when the variable aq is available in the URL or false when not present. The three other properties will return null because they are not set in the example.
The above works as expected, but I need to do this in another class, lets again call it class b. The arguments will be set to class b, and in turn, class b will set this arguments to class Referrer, class Referrer will use this arguments and return the proper values of its properties, and class b will use this results to do something else
Example:
$q = new b(['authorReferrer' => 'aq']);
Where class b could be something like this (it is this part that I'm not sure how to code)
class b implements bInterface
{
protected $w;
protected $other;
public function __construct($args = [])
{
//Do something here
// Do something here so that we can use $other in other classes or functions
}
public function a()
{
$w = new Referrer($args);
}
public function b()
{
// use $w properties here
// return $other for usage in other classes and functions
}
}
The best way is to inject the referrer to your classes in order to do loose coupling between them and the referrer (this pattern use the benefit of your ReferrerInterface):
class b implements bInterface
{
protected $referrer;
public function __construct(ReferrerInterface $referrer, array $values = array())
{
$this->referrer = $referrer;
$this->referrer->setBulk($values);
}
public function getReferrer()
{
return $this->referrer;
}
public function b()
{
// use $this->referrer properties here
}
}
// Instantiation (use your dependency injection if you have one):
$referrer = new Referrer();
$b = new b($referrer, ['authorReferrer' => 'aq']);
I do not understand what is $other so I removed it but explain me if you want me to I add it again.
If you need to use the properties of the referrer in b, you should add some getters in your ReferrerInterface to allow that. I would use setAuthorReferrer($isAuthorReferrer) to set the value and isAuthorReferrer() to get it for instance.
I have the following two classes (I have not included the interfaces)
ConditionsRefer
namespace PG\Referrer\Single\Post;
class ConditionsRefer implements ConditionsReferInterface
{
/**
* #var $authorReferrer = null
*/
private $isAuthorReferrer = null;
/**
* #var $dateReferrer = null
*/
private $isDateReferrer = null;
/**
* #var $searchReferrer = null
*/
private $isSearchReferrer = null;
/**
* #var $taxReferrer = null
*/
private $isTaxReferrer = null;
/**
* #param array $values = null;
*/
public function __construct(array $values = null)
{
if ($values)
$this->setBulk($values);
}
/**
* Bulk setter Let you set the variables via array or object
*/
public function setBulk($values)
{
global $wp_query;
if (!is_array($values) && !$values instanceof \stdClass) {
throw new \InvalidArgumentException(
sprintf(
'%s needs either an array, or an instance of \\stdClass to be passed, instead saw %s',
__METHOD__,
is_object($values) ? get_class($values) : gettype($values)
)
);
}
foreach ($values as $name => $value) {//create setter from $name
if (array_key_exists($value, $wp_query->query_vars)) { //Check that user don't set a reserved query vars
throw new \InvalidArgumentException(
sprintf(
'%s is a reserved query_vars and cannot be used. Please use a unique value',
$value
)
);
}
$setter = 'set' . $name;
$condition = isset($_GET[$value]);
if ($setter !== 'setBulk' && method_exists($this, $setter)) {
$this->{$setter}($condition);//set value (bool)
}
}
return $this;
}
/**
* #param $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer)
{
$this->isAuthorReferrer = $isAuthorReferrer;
return $this;
}
/**
* #param $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer)
{
$this->isDateReferrer = $isDateReferrer;
return $this;
}
/**
* #param $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer)
{
$this->isSearchReferrer = $isSearchReferrer;
return $this;
}
/**
* #param $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer)
{
$this->isTaxReferrer = $isTaxReferrer;
return $this;
}
}
QueryArgumentsRefer
namespace PG\Referrer\Single\Post;
class QueryArgumentsRefer implements QueryArgumentsReferInterface
{
private $referrer;
public function __construct(ConditionsReferInterface $referrer, array $values = array())
{
$this->referrer = $referrer;
$this->referrer->setBulk($values);
}
public function getReferrer()
{
return $this->referrer;
}
public function b()
{
$test = (object) $this->referrer;
if($test->isAuthorReferrer === false)
return 'This is just a test';
}
}
This is how I use it in a file
$a = new QueryArgumentsRefer(new ConditionsRefer(), ['authorReferrer' => 'aq']);
?><pre><?php var_dump($a->b()); ?></pre><?php
In function b() in class QueryArgumentsRefer, I need to use the properties of class ConditionsRefer.
This is the result of $test, which is the expected result, so this is working
object(PG\Referrer\Single\Post\ConditionsRefer)#522 (4) {
["isAuthorReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
bool(false)
["isDateReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
["isSearchReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
["isTaxReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
}
If I try to use $test->isAuthorReferrer, I get the following error
Fatal error: Cannot access private property PG\Referrer\Single\Post\ConditionsRefer::$isAuthorReferrer
which is expected I guess. The only way to make this work in my mind is setting the properties in ConditionsRefer to public
I've read properties should be private, and not public. How can I properly work around this problem, or do I have to make my properties public
EDIT
I have tried setting my properties to protected, but that does noet help as this also gives me a fatala error
Use protected and your child classes can use it. This way you can still have access to it in children classes without making it public. private means only the base class may use it.
Also, as a side note, all variables in a class without a default value will default to null
/**
* #var $authorReferrer = null
*/
protected $isAuthorReferrer;
Solve this issue. I'm still new to OOP and had a slight misunderstanding about setters and getter.
What I did is, I created a getter for each setter in the ConditionsRefer class, and instead of trying to use the properties of this class in the QueryArgumentsRefer class (which caused the initial error), I used the getters to get my info from the ConditionsRefer class inside the QueryArgumentsRefer class like
$this->conditionalReferrer->isAuthorReferrer();
im using Codeigniter 3.0 query builder, my question when ever my model return a user i'm returning a database row. not an object -its stdobject but not try object- is this anything related to oop practice ?
my auth model is simple
class user extend CI_MODEL{
funciton attempt($user,$pass){
//do validation and fetch user and compare pass etc...
$query = $this->db->get_where('users',$where);
return $query->result() //now this is my line of question
}
}
so i think this is nothing related to oop ? -or am i wrong ? - its just procedural code using classes for organization !.
so what is the correct way in oop manner ?
I have goan through many auth libraries for codeigntier to see how they do it, and all what i see is they save user row to an array variable in model. yet all users are still inside only 1 user object .
should i create an abstract class/interfaces for user object and pass database row to it every time i fetch a user before i save them to my Big ci_model ?
if so is this doable in codeigniter ? where would i put this abstract classes ?
I have done somthing like this, and yes I have created a model_row class to pass all data to in a array_walk style:
if ($qry = $this->db->get()) {
$res = $qry->result();
return $this->_instantiateRows($res);
}
Function _instantiateRows():
/**
* Get the row class name
* Checks if a class exists with the model name _Row
* #return string
*/
private function _getRowClass() {
$modelName = get_class($this);
return class_exists($modelName.'_Row') ? $modelName.'_Row' : 'Model_Row';
}
/**
* Formats results into model row classes
* #param array $results
* #return array
*/
protected function _instantiateRows($results) {
$rowClass = $this->_getRowClass();
$self = $this;
array_walk($results,function(&$row,$k) use ($rowClass, $self) {
$row = new $rowClass($row,$self,$k);
});
return $results;
}
Then a row class:
/**
* Model row class
* Acts as a baseclass and a fallback class for database rows
* Implements standard methods, for saving, getting the ID, and setting field
* values.
* #property $_model MY_Model
* #property $_key Original Array key
* #property $_ID_FIELD name of the id field
*/
class Model_Row
{
protected $_isNew = True;
protected $_model = False;
protected $_key = False;
protected $_ID_FIELD = 'id';
protected $_ID_ORIGINAL = False;
/**
* C'tor
* Sets up the object with data from the row
* #param object $data
* #param object $model
* #param int $key
* #param string $id_field
*/
public function __construct($data,$model,$key=False) {
$this->_key = $key;
// If no key is specified then it must be new / Not from database
if ($this->_key !== False)
$this->_isNew = False;
$data = (array)$data;
$this->_model = $model;
$this->_ID_FIELD = $model->idField;
$this->set($data);
// Ensure ID Field is set.
$idF = $this->_ID_FIELD;
if (!isset($this->$idF))
$this->$idF = null;
}
/**
* Get the ID field
* ID Field could be named differently for each model, this is an easy
* shortcut to it.
* #param string $setTo - set the id to this value
* #return string
*/
public function id($setTo=False) {
$idF = $this->_ID_FIELD;
if ($setTo !== False) {
if ($this->_ID_ORIGINAL === False && !$this->_isNew)
$this->_ID_ORIGINAL = $this->$idF;
$this->set($idF,$setTo);
}
return $this->$idF !== null ? (string)$this->$idF : False;
}
/**
* Save this row
* #return bool
*/
public function save() {
$wheres = array();
if (!$this->_isNew) {
$idF = $this->_ID_FIELD;
$wheres[$idF] = $this->_ID_ORIGINAL ?: $this->id();
}
$res = $this->_model->set($this,$wheres);
if ($this->id() === False)
$this->id($this->_model->insertID());
// Reset the original id field
$this->_ID_ORIGINAL = False;
$this->_isNew = False;
if ($res)
return $this;
return False;
}
/**
* Set object properties
* can be passed by array field => value
* #param mixed $field
* #param mixed $value
* #return null
*/
public function set($field,$value=False) {
if ((is_array($field) || is_object($field)) && $value === False) {
if (is_object($field))
$field = (array)$field;
foreach($field as $f => $v)
$this->set($f,$v);
}
else {
if (method_exists($this, 'set_'.$field))
call_user_func(array($this,'set_'.$field), $value);
else
$this->$field = $value;
}
}
}
The point of the _getRowClass is to check for a class called model_name_row if this exists, then instantiate the data to this class, otherwise fall back to the baseclass model_row
There are some other things your model will need too, because the row class will be passed the model, so your model will need a public $idField='id' , and then this function can be usefull on your model:
/**
* Create a new record using the model row class
* #param mixed $data
* #return Model_Row
*/
public function newRow($data=array()) {
$rowClass = $this->_getRowClass();
return new $rowClass($data,$this);
}
So you can do $newRow = $this->Model->newRow($data) which will create a new row, then can call $newRow->save() and other methods if set...
* EDIT
Also to point out, I use $this->_model->set($this, $wheres) on the row class, this is because I have defined a baseclass with a public setter:
/**
* Setter
* #param mixed $data object or array
* #param mixed $wheres object or array query
* #return bool
*/
public function set($data,$wheres=array()) {
if (!$this->checkTableSet())
return False;
if (empty($wheres)) {
return $this->db->insert($this->table,$data);
}
else {
$this->db->where($wheres);
return $this->db->update($this->table,$data);
}
}
$this->table is a model variable with the table name, e.g. protected $table='users'; and the function checkTableSet() simply checks whether this has been set and not empty..
Hello i'm having some trouble with __get __set in php 5.3 it seems that i have an error there.. already googled about it and seems it's a change of behaviour since php 5 but i didn't find a solution that would work :-(
$config['url']['uri'] = $request->uri();
=>
Notice: Indirect modification of overloaded element of Library\Config\Config has no effect in subdomains/prod/index.php on line 33
use \ArrayObject;
use \ArrayAccess;
use \Countable;
use \IteratorAggregate;
use \ArrayIterator;
use \Twelve\SingletonPattern\LazySingleton;
use \Twelve\SingletonPattern\Interfaces\ILazySingleton;
/**
* Singleton With Configuration Info
*/
class Config extends ArrayObject implements ArrayAccess, Countable, IteratorAggregate, ILazySingleton
{
/**
* Instance Var
* #var Config
*/
protected static $_instance = null;
/**
* Stores FileName
* #var Config
*/
protected static $_configFile = '';
/**
* Config Settings Array
* #var Config
*/
protected $_settings = array();
public static function getInstance(){
return LazySingleton::getInstance(__CLASS__);
}
/**
* Set the Config File Path
*/
public static function setFile($filePath)
{
static::$_configFile = $filePath;
}
public function __construct()
{
LazySingleton::validate(__CLASS__);
// Allow accessing properties as either array keys or object properties:
parent::__construct($this->_settings, ArrayObject::ARRAY_AS_PROPS);
$values = include(static::$_configFile);
if(is_array($values))
{
$this->_settings = $values;
}
}
/**
* Prevent Cloning
*/
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
// No Cloning Allowed
}
/**
* Returns the Number of Elements Inside the Config File
* #var Config
* #return Integer Number of Elements Inside Config
*/
public function count()
{
return sizeof($this->_settings);
}
/**
* Check if a Given Key Exists
* #var Config
* #param mixed $offset Key of Item to Check
* #return Boolean True if Key Exists, False Otherwise
*/
public function offsetExists($offset)
{
return key_exists($offset, $this->_settings);
}
/**
* Retrieve the Value of a Given Key
* #param mixed $offset Key of Item to Fetch
* #return mixed Value of the Matched Element
*/
public function offsetGet($offset)
{
return $this->_settings[$offset];
}
/**
* Assign a new Value to a Key
* #param mixed $offset Key of the Element to Set
* #param mixed $value Value to Assign
*/
public function offsetSet($offset, $value)
{
$this->_settings[$offset] = $value;
}
/**
* Remove an Item from the Config
* #param mixed $offset Key of the Element to Remove
*/
public function offsetUnset($offset)
{
unset($this->_settings[$offset]);
}
/**
* Retrieve an Iterator for Config Values
* #return Iterator Iterator of Config Values
*/
public function getIterator()
{
return new ArrayIterator($this->_settings);
}
/**
* Enables to Set Values Using the Object Notation i.e $config->myValue = 'Something';
*/
public function __set($key, $value)
{
(array) $this->_settings[$key] = $value;
}
/**
* Enables to Get Values using the Object Notation i.e $config->myValue;
*/
public function &__get($key)
{
return $this->_settings[$key];
}
public function __isset($key)
{
return isset($this->_settings[$key]);
}
}
New Edit:
parent::__construct($this->_settings, ArrayObject::ARRAY_AS_PROPS);