Best way to create associative array of unique objects in PHP? - php

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.

Related

Initializing class properties

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.

Php, isset - default value, can it be simplified?

here is something:
$result = $this->getSomething();
$db = new Db();
$db->save($result['DATA']); // might exists or not
$db->save($result['IP']); // might exists or not
$db->save($result['X']); // might exists or not
but those array keys are not sure to be exists. Of corse I can always write this:
$result = $this->getSomething();
if (!isset($result['DATA']))
{
$result['DATA'] = null;
}
//same for the other keys
$db = new Db();
$db->save($result['DATA']);
$db->save($result['IP']);
$db->save($result['X']);
but its very cumbersome job. Is there any way to simplify this?
Starting at PHP 7 (which every should start using anyway), you can use the new coalesce operator to do this:
$db->save($result['DATA'] ?? null);
$result = $this->getSomething();
$result += array_fill_keys(['DATA', 'IP', 'X'], null);
This populates any of these keys which don't exist with null.
Can even be written as $result = $this->getSomething() + array_fill_keys(..);.
+ array union operator
array_fill_keys
You can do this:
$data = !isset($result['DATA']) ? null : $result['Data'];
$ip = !isset($result['IP']) ? null : $result['IP'];
etc...
It would be better if you would be use OOP way
your method getSomething() must return instance of some class (ex. SomeData) instead of array
/**
* #return SomeData
*/
public function getSomething()
{
/*
* some code
*/
return new SomeData($data, $x, $ip);
}
SomeData class
class SomeData
{
private $data;
private $ip;
private $x;
public function __construct($data, $ip, $x)
{
$this->data = $data;
$this->ip = $ip;
$this->x = $x;
}
/**
* #return mixed
*/
public function getData()
{
return $this->data;
}
/**
* #param mixed $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* #return mixed
*/
public function getIp()
{
return $this->ip;
}
/**
* #param mixed $ip
*/
public function setIp($ip)
{
$this->ip = $ip;
}
/**
* #return mixed
*/
public function getX()
{
return $this->x;
}
/**
* #param mixed $x
*/
public function setX($x)
{
$this->x = $x;
}
}
And finaly you work with result without worrying about isset and etc.
$result = $this->getSomething();
$db = new Db();
$db->save($result->getData());
$db->save($result->getIp());
$db->save($result->getX());

How to add more value to a single array in the same function?

When I try the following code, I get just the first output. I wanted to append all the outputs to the array self::$results so that the same function will return that array, but after running the script, the function returns the array but only the first output. That means, it appended only the the first output.
<?php
/**
* #author Ewoenam
* #copyright 2014
*
* #odj class book
*/
class book
{
/**
* #array static $alpha; holds array of accepted words
*/
public static $alpha = array();
/**
* #array static $result; holds array of correct answers
*/
public static $results;
/**
* #string protected $word; parametr
*/
protected $word;
/**
* #var static $alpha; holder of class instance getter
* returns self::getInstance
*/
public static $init;
/**
* #method static getInstance(); class instance getter
* returns self();
*/
function __construct()
{
self::$alpha = array('love','life','health','power','money','God');
}
public static function getInstance()
{
if (self::$init === null)
{
self::$init = new self();
return self::$init;
}
}
/**
* #method static check()
* takes 1 param; self::$word
* returns bool
*/
public static function check($word)
{
for($i=0;$i<=count(self::$alpha)-1;$i++)
{
if(similar_text($word,self::$alpha[$i]) === strlen($word))
{
return true;
}
}
}
/**
* #method static result()
* takes 1 param; self::check()
* returns bool
*/
public static function result($bool)
{
if($bool === true)
{
return 'correct';
}
else
{
return 'wrong';
}
}
/**
* #method static getter()
* takes 1 param; array of words to be searched
* returns array self::$results
*/
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
// i want to add more thn one answers to to $result array but i get only the first answer.
//how do i ddo it?
self::$results[] = self::result(book::check($array[$i]));
return self::$results;
}
}
}
$array = array('love','ama','kofi','money','health','God');
print_r(book::getInstance()->getter($array));
var_dump(book::check('love')) ;
?>
You're returning from $getter inside the loop, so it returns after adding the first element. You should finish the loop and then return:
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
self::$results[] = self::result(book::check($array[$i]));
}
return self::$results;
}
You have your return in the for loop. That's why when it goes through the first time, it returns the function right there instead of continuing the loop.
Change your function to this and it will work how you want it to:
/**
* #method static getter()
* takes 1 param; array of words to be searched
* returns array self::$results
*/
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
// i want to add more thn one answers to to $result array but i get only the first answer.
//how do i ddo it?
self::$results[] = self::result(book::check($array[$i]));
}
return self::$results;
}

How to construct constants in yii

I am using Yii for my web application. In this I kept Constants class in model and extended
from CUserIdentity like..
class Constants extends CUserIdentity
{
CONST ACCOUTN_ONE = 1;
CONST ACCOUTN_TWO = 2;
CONST ACCOUTN_THREE = 3;
}
Here I can access constants like Constants::ACCOUTN_ONE and it will return correct result as 1
But when I start construct constants dynamically means..
$type = 'ONE';
$con = "Constants::ACCOUTN_".$type;
echo $con;
It will dispaly as Constants::ACCOUTN_ONE;
I am expecting here 1
Please correct me if any mistake..
$type = 'ONE';
$con = "Constants::ACCOUTN_".$type;
echo Constant($con);
$type = 'ONE'; // You created string
$con = "Constants::ACCOUTN_".$type; // Created other string
echo $con; // Printed it
You just printed string without evaluating it.
Yes, of couse it will dispaly as Constants::ACCOUTN_ONE;
You need to evaluate your code with eval()(bad), or use this scheme:
echo Constant($con);
I do this with
a class for it a while back:
/**
* Lots of pixie dust and other magic stuff.
*
* Set a global: Globals::key($vlaue); #return void
* Get a global: Globals::key(); #return mixed|null
* Isset of a global: Globals::isset($key); #return bool
*
* Debug to print out all the global that are set so far: Globals::debug(); #return array
*
*/
class Globals
{
private static $_propertyArray = array();
/**
* Pixie dust
*
* #param $method
* #param $args
* #return mixed|bool|null
* #throws MaxImmoException
*/
public static function __callStatic($method, $args)
{
if ($method == 'isset') {
return isset(self::$_propertyArray[$args[0]]);
} elseif ($method == 'debug') {
return self::$_propertyArray;
}
if (empty($args)) {
//getter
if (isset(self::$_propertyArray[$method])) {
return self::$_propertyArray[$method];
} else {
return null; //dont wonna trow errors when faking isset()
}
} elseif (count($args) == 1) {
//setter
self::$_propertyArray[$method] = $args[0];
} else {
throw new Exception("Too many arguments for property ({$method}).", 0);
}
}
}

Which is the best way to display 'flash messages' in kohana v3?

I would like to know the best way to display flash messages in Kohana v3?
Some tutorials or examples would be helpful.
Do you mean like Kohana 2.x's flash session variables?
The latest Kohana supports get_once() which is pretty similar to the old flash session variables.
$session = Session::instance();
$session->set('test', 'Hello, World!');
// The session variable is returned and removed.
$test = $session->get_once('test');
I think the get_once is a great function, but what if you want to keep the data actually separate from the regular data, here's a basic class that overloads "Session" so that you can use "codeigniter" style flashdata calls with any data-store.
<?php defined('SYSPATH') or die('No direct script access.');
abstract class Session extends Kohana_Session {
/**
* This calls the parent Kohana_Session constructor and processes
* new flashdata to flashdata, and flashdata to old flashdata
*
* #param array configuration
* #param string session id
* #return void
* #uses Kohana_Session::__construct
*/
public function __construct(array $config = NULL, $id = NULL)
{
parent::__construct($config,$id);
if(array_key_exists('___of',$this->_data)){
//Remove old Flash data
unset($this->_data['___of']);
}
if(array_key_exists('___flash',$this->_data)){
//Move current last requests flash data to old flash data
$this->_data['___of'] = $this->_data['___flash'];
unset($this->_data['___flash']);
}
if(array_key_exists('___nf',$this->_data)){
//Move Last Requests added data to the flash data
$this->_data['___flash'] = $this->_data['___nf'];
unset($this->_data['___nf']);
}
}
/**
* keeps a variable set in the sessions flashdata array.
*
* $session->set_flashdata('foo', 'bar');
*
* #param string variable name
* #param ...
* #return $this
*/
public function keep_flashdata($k)
{
$args = func_get_args();
if(array_key_exists('___of',$this->_data)){
foreach($args as $key){
if(array_key_exists($key,$this->_data['___of'])){
//So we were going to trash it...
$this->set_flashdata($k,$this->_data['___of'][$key],true);
}
}
}
$this->_data['___nf'][$key] = $value;
return $this;
}
/**
* Set a variable in the sessions flashdata array.
*
* $session->set_flashdata('foo', 'bar');
*
* #param string variable name
* #param mixed value
* #return $this
*/
public function set_flashdata($key, $value, $current=false)
{
if(!array_key_exists('___nf',$this->_data)){
$this->_data['___nf'] = array();
}
$this->_data['___nf'][$key] = $value;
if($current){
if(!array_key_exists('___flash',$this->_data)){
$this->_data['___flash'] = array();
}
$this->_data['flash'][$key] = $value;
}
return $this;
}
/**
* Set a variable by reference in the sessions flashdata array.
*
* $session->bind_flashdata('foo', $foo);
*
* #param string variable name
* #param mixed referenced value
* #return $this
*/
public function bind_flashdata($key, & $value)
{
if(!array_key_exists('___nf',$this->_data)){
$this->_data['___nf'] = array();
}
$this->_data['___nf'][$key] =& $value;
return $this;
}
/**
* Removes a variable in the session array.
*
* $session->delete_flashdata('foo');
*
* #param string variable name
* #param ...
* #return $this
*/
public function delete_flashdata($key)
{
$args = func_get_args();
if(array_key_exists('___nf',$this->_data)){
foreach ($args as $key)
{
if(array_key_exists($key,$this->_data['___nf'])){
unset($this->_data['___nf'][$key]);
}
}
}
return $this;
}
/**
* Get a variable from the sessions flashdata array.
*
* $foo = $session->get_flashdata('foo');
*
* #param string variable name
* #param mixed default value to return
* #return mixed
*/
public function get_flashdata($key, $default = NULL)
{
if(array_key_exists('___flash',$this->_data) && array_key_exists($key,$this->_data['___flash'])){
return $this->_data['___flash'][$key];
} else if(array_key_exists('___nf',$this->_data) && array_key_exists($key,$this->_data['___nf'])){
return $this->_data['___nf'][$key];
}
return $default;
}
/**
* Get and delete a variable from the session array.
*
* $bar = $session->get_once('bar');
*
* #param string variable name
* #param mixed default value to return
* #return mixed
*/
public function get_flashdata_once($key, $default = NULL)
{
$value = $this->get_flashdata($key, $default);
if(array_key_exists($key, $this->_data['___flash'])){
unset($this->_data['___flash'][$key]);
}
if(array_key_exists($key, $this->_data['___nf'])){
unset($this->_data['___nf'][$key]);
}
return $value;
}
}
?>
I realize there was an answer to this, and like i stated before, the get_once method is great and all, but i enjoy auto garbage collection much more.
If you have any improvements on this code, let me know, its been great to me so far.
Have a look at this module, it might be what you are looking for https://github.com/daveWid/message
I've written a really simple class for this once. Check it out below. Usage examples below
class Notice {
private static $session;
private static $initialized = false;
// current notices
private static $notices = array();
function __construct() {
}
static function init() {
self::$session = Session::instance();
self::$notices['current'] = json_decode(self::$session->get_once('flash'));
if(!is_array(self::$notices['current'])) self::$notices['current'] = array();
self::$initialized = true;
}
static function add($notice, $key=null) {
if(!self::$initialized) self::init();
if(!is_null($key)) {
self::$notices['new'][$key] = $notice;
} else {
self::$notices['new'][] = $notice;
}
self::$session->set('flash', json_encode(self::$notices['new']));
return true;
}
static function get($item = null) {
if(!self::$initialized) self::init();
if($item == null) {
return self::$notices['current'];
}
if(!array_key_exists($item, self::$notices['current']))
return null;
return self::$notices['current'][$item];
}
}
Examples (provided this class is saved as APPPATH . 'classes/notice.php'):
Notice::add('Something great has happened!');
Notice::add('Exciting! I\'ve got something to tell you!', 'message');
echo Notice::get('message'); // "Exciting! I've got ..."
foreach(Notice::get() as $message) {
echo $i++ . $message .'<br />';
}
EDIT: funny... for some reason this question popped up somewhere, didn't notice it was a really old one... sorry for that!
I am using https://github.com/synapsestudios/kohana-notices in my project and I am very happy with it.

Categories