Should I create this accessors - php

So I got this example layout.
private $_getMilk() = '';
public function getMilk():string {
return $this->_milk;
}
public function setMilk(string $milk) {
$this->_milk = $milk;
}
SetMilk is also used to empty milk which sounds weird to me why set empty string if you ask for milk.
Should I instead also create the function emptyMilk. (asume the milk property is getting called alot)
public function emptyMilk() {
$this->_milk = '';
}

A benefit of a seperate emptyMilk() function is that it allows you to use a special representation for an empty object, rather than exposing that to the callers.
private $is_empty = true;
public function getMilk(): string {
if ($this->$is_empty) {
throw new Exception("Empty milk");
}
return $this->$_milk;
}
public function setMilk(string $milk) {
$this->is_empty = false;
$this->_milk = $milk;
}
public function emptyMilk() {
$this->is_empty = true;
$this->_milk = null;
}
public function gotMilk(): boolean {
return !$this->is_empty;
}
This allows you to use any value for $_milk rather than making one value special.

Related

Value keeps overriding my old value in a session array

I have a very simple random number generator. This number generator has a few options: you input a minimum and maximum number, form this number a random number will be generated. I store the maximum-minimum and generated number inside of a session to be used later while playing the game.
When we get redirected to the main page (the guess game) you have the option to guess the random number that is stored in the session from their one forth the game will give you feedback on how close you are etc.
My question boils down to this, an option of the game is to display all your previous guesses I had a basic idea of how I wanted to do this: I will store the current guess I did inside of an array this array will be linked to a session from which I can loop through all of the old guesses.
I have written a couple of functions to achieve this and it only stores the current guess overriding any number already stored in the array e.g. if my current guess is 6 this number will be stored inside the array, if I proceed to guess again for example 4 this number will be stored inside of the array but will override 6.
I hope my code examples will be more clear.
this function stores the old guesses inside of said array
public function Store_Old_Guesses_In_Session()
{
if (!isset($_SESSION["show_previous_guesses"])) {
return false;
}
if (isset($_POST["Guess_Button"])) {
return $_SESSION["old_guesses"] = $this->old_guesses = array(
"Old_guesses" => $this->getCurrentGuess(),
);
}
return false;
}
this function gets the current guess
public function getCurrentGuess()
{
if (isset($this->current_guess)) {
return $this->current_guess;
}
return false;
}
-
namespace Store;
use Get_Number\Get_Numbers;
require_once "Current_Guesses.php";
class Store_Numbers extends Get_Numbers
{
public array $old_guesses;
public function __construct()
{
parent::__construct();
if (isset($_SESSION["old_guesses"])) {
$this->old_guesses = $_SESSION["old_guesses"];
}
}
public function Store_Current_Number_In_Session()
{
if (empty($this->getCurrentNumber())) {
return false;
}
return $_SESSION["current_number"] = $this->getCurrentNumber();
}
public function Store_Minimum_Number_In_Session()
{
if (empty($this->getMinimumNumber())) {
return false;
}
return $_SESSION["minimum_number"] = $this->getMinimumNumber();
}
public function Store_Maximum_Number_In_Session()
{
if (empty($this->getMaximumNumber())) {
return false;
}
return $_SESSION["maximum_number"] = $this->getMaximumNumber();
}
public function Store_Checkbox_Value_In_Session()
{
if (empty($_POST["debug_options"])) {
return false;
}
return $_SESSION["debug_options"] = $_POST["debug_options"];
}
public function Store_show_previous_guesses_Checkbox_In_Session()
{
if (empty($_POST["show_previous_guesses"])) {
return false;
}
return $_SESSION["show_previous_guesses"] = $_POST["show_previous_guesses"];
}
public function Store_Current_Number_Checkbox_Value_In_Session()
{
if (empty($_POST["current_number_checkbox"])) {
return false;
}
return $_SESSION["current_number_checkbox"] = $_POST["current_number_checkbox"];
}
public function Store_Old_Guesses_In_Session()
{
if (!isset($_SESSION["show_previous_guesses"])) {
return false;
}
if (isset($_POST["Guess_Button"])) {
$this->old_guesses[] = $this->getCurrentGuess();
$_SESSION['old_guesses'] = array_push($this->old_guesses, $this->getCurrentGuess());
return $this->old_guesses;
}
return false;
}
}
I have a bunch of code examples should you need more
A few things you might need to know:
PHP = 7.4.2
The property $old_guesses is declared as an array like this public
array $old_guesses;
You need to push onto the array, not assign to it.
public function Store_Old_Guesses_In_Session()
{
if (!isset($_SESSION["show_previous_guesses"])) {
return false;
}
if (isset($_POST["Guess_Button"])) {
$this->old_guesses[] = $this->getCurrentGuess();
$_SESSION['old_guesses'] = $this->old_guesses;
return $this->old_guesses;
}
return false;
}
The constructor should initialize the variable from the session variable to make it persist and grow.
if (!isset($_SESSION['old_guesses'])) {
$_SESSION['old_guesses'] = array();
}
$this->old_guesses = $_SESSION['old_guesses'];

How to check returned value to which function it belogns

Say I have to similar function :
public function auth(){
return $someResponse;
}
public function collect(){
return $someOtherResponse
}
Question : When one of the response get passed to another class, is there any way to check which function returned the response ?
In a purely object-oriented way, wanting to attach information to a value is akin to wrapping it into a container possessing context information, such as:
class ValueWithContext {
private $value;
private $context;
public function __construct($value, $context) {
$this->value = $value;
$this->context = $context;
}
public value() {
return $this->value;
}
public context() {
return $this->context;
}
}
You can use it like this:
function auth()
{
return new ValueWithContext($someresponse, "auth");
}
function collect()
{
return new ValueWithContext($someotherrpesonse, "collect");
}
This forces you to be explicit about the context attached to the value, which has the benefit of protecting you from accidental renamings of the functions themselves.
As per my comment, using arrays in the return will give you a viable solution to this.
It will allow a way to see what has been done;
function auth()
{
return (array("auth" => $someresponse));
}
function collect()
{
return (array("collect" => $someotherrpesonse));
}
class myClass
{
function doSomething($type)
{
if (function_exists($type))
{
$result = $type();
if (isset($result['auth']))
{
// Auth Used
$auth_result = $result['auth'];
}
else if (isset($result['collect']))
{
// Collect used
$collect_result = $result['collect'];
}
}
}
}
It can also give you a way to fail by having a return array("fail" => "fail reason")
As comments say also, you can just check based on function name;
class myClass
{
function doSomething($type)
{
switch ($type)
{
case "auth" :
{
$result = auth();
break;
}
case "collect" :
{
$result = collect();
break;
}
default :
{
// Some error occurred?
}
}
}
}
Either way works and is perfectly valid!
Letting the two user defined functions auth() & collect() call a common function which makes a call to debug_backtrace() function should do the trick.
function setBackTrace(){
$backTraceData = debug_backtrace();
$traceObject = array_reduce($backTraceData, function ($str, $val2) {
if (trim($str) === "") {
return $val2['function'];
}
return $str . " -> " . $val2['function'];
});
return $traceObject;
}
function getfunctionDo1(){
return setBackTrace();
}
function getfunctionDo2(){
return setBackTrace();
}
class DoSomething {
static function callfunctionTodo($type){
return (($type === 1) ? getfunctionDo1() : getfunctionDo2());
}
}
echo DoSomething::callfunctionTodo(1);
echo "<br/>";
echo DoSomething::callfunctionTodo(2);
/*Output
setBackTrace -> getfunctionDo1 -> callfunctionTodo
setBackTrace -> getfunctionDo2 -> callfunctionTodo
*/
The above function would output the which function returned the response

How to properly use properties of a class as arguments of its methods

I want to use a private method in a class called by public methods from the same class.
class Foo
{
private $updateAll = true;
private $updateA = false;
private $updateB = false;
public function updateA()
{
$this->updateAll = false;
$this->updateA = true;
$this->updateB = false;
$this->update();
}
public function updateB()
{
$this->updateAll = false;
$this->updateA = false;
$this->updateB = true;
$this->update();
}
private function update()
{
// Code here...
if ($this->updateAll) {
// set all api call params
}
if ($this->updateA) {
// set api call param
}
if ($this->updateB) {
// set api call param
}
// Code here...
}
}
Is this a proper use of class properties as arguments?
It works but I don't know whether there is a better way to do this. My purpose is to kinda use dynamic method arguments without the need to pass 3 arguments to update().
Your code is not wrong and should work just fine as you say, but it does feel a bit weird... I think any of the following approaches is cleaner and more flexible than your code. I'm sure there will be lots of other options perfectly valid, these are just some ideas...
Multiple method arguments
As it's been suggested to you in the comments, I think the normal way to do that is actually just adding the arguments to the update() method...
class Updater
{
public function update($all = true, $a = false, $b = false)
{
// Code...
}
}
One constant method argument
However, in your example, the options seem to be mutually exclusive (any combination of 2 options is redundant), so you can do perfectly fine with just one parameter!
class Updater
{
const UPDATE_ALL = 'all';
const UPDATE_A = 'a';
const UPDATE_B = 'b';
public function update($updateMode = self::UPDATE_ALL)
{
// Code...
}
}
Command pattern
If your example is not realistic, and you have a scenario with lots of options that are not mutually exclusive, I'd use something similar to a command pattern, where the class in charge to define the options of the operations is different from the class that performs the operation...
class Updater
{
public function update(UpdateCommand $command)
{
// Code...
}
}
class UpdateCommand
{
public $paramA = false;
public $paramB = false;
// ...
public $paramZ = false;
}
Fluent interface
Or you could also use a fluent interface. Although that's a bit harder to test...
class Updater
{
private $paramA = false;
private $paramB = false;
// ...
private $paramZ = false;
public function withA()
{
$this->paramA = true;
return $this;
}
public function withB()
{
$this->paramB = true;
return $this;
}
// ...
public function withZ()
{
$this->paramZ = true;
return $this;
}
public function run()
{
// Code...
}
}

How will i pass an array as key to __set magic method

I have a php singleton session class as follows.
class Session {
static private $_instance = NULL;
private function __construct()
{
session_start();
}
/**
* Prevents the class from being cloned
* #return NULL
*/
private function __clone() { }
/**
* Returns the singleton instance of this class
* #return Session
*/
public static function getInstance()
{
if (!self::$_instance) {
self::$_instance = new Session();
}
return self::$_instance;
}
public function __get($key) {
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
}
return NULL;
}
public function __set($key, $value)
{
$_SESSION[$key] = $value;
}
public function __isset($key) {
return isset($_SESSION[$key]);
}
public function __unset($key) {
unset($_SESSION[$key]);
}
}
I can create an object as follows
$session = Session::getInstance();
$session->name = 'some name';
I can also get the value like
echo $session->name;
The problem is, i want to pass an array to this object and it is not working. for example, i wan to set something like
$_SESSION['user']['data'] = array('name'=>'some name',"empId"=>'123');
I am trying like this.
$session->['user']['data'] = array('name'=>'some name',"empId"=>'123');
but it is not working. Could you please suggest what is wrong.
The workaround in this case would be to use:
public function &__get($key) {
if (isset($_SESSION[$key])) {
return & $_SESSION[$key];
}
return NULL;
}
You need to modify the __get() method, because an assignment like
$session->user['data'] = ...
will actually retrieve the [user] key, and then try to assign a new subarray [data] to that temporary array result.
Also note that $session->['user']['data'] is invalid syntax. You either need $session->user['data'] or $session->{'user'}['data'].
Anyway, I think it is probably not a good idea to use a wrapper if you often want to do assignments like that. (I do actually have something very similar.)
$session->user = array('data' => array('name'=>'some name',"empId"=>'123'));
Make sure you don't overwrite anything else in user you want to keep

How to return false when chaining methods

I have a validation class which uses method chaining. I would like to be able to do single checks with TRUE/FALSE like this:
if ($obj->checkSomething()) {}
But also chain methods like this:
if ($obj->checkSomething()->checkSomethingElse()) {}
The problem however is that if one method returns FALSE, it will not send back an object and thus breaks the method chaining which ends with this error:
Fatal error: Call to a member function checkSomething() on a non-object in ...
Do I have to pick either single method return calls or method chaining or is there a workaround?
One idea would be to set an internal flag to indicate success or failure, and access it via another method, while checking that flag in each method and not doing anything if it's set. E.g.:
class A {
private $valid = true;
public function check1() {
if (!$this->valid) {
return $this;
}
if (!/* do actual checking here */) {
$this->valid = false;
}
return $this;
}
public function check2() {
if (!$this->valid) {
return $this;
}
if (!/* do actual checking here */) {
$this->valid = false;
}
return $this;
}
public function isValid() {
return $this->valid;
}
}
// usage:
$a = new A();
if (!$a->check1()->check2()->isValid()) {
echo "error";
}
To minimize the boilerplate checking in each function, you could also use the magic method __call(). E.g.:
class A {
private $valid;
public function __call($name, $args) {
if ($this->valid) {
$this->valid = call_user_func_array("do" . $name, $args);
}
return $this;
}
private function docheck1() {
return /* do actual checking here, return true or false */;
}
private function docheck2() {
return /* do actual checking here, return true or false */;
}
public isValid() {
return $this->valid;
}
}
The usage would be same as above:
$a = new A();
if (!$a->check1()->check2()->isValid()) {
echo "error";
}
I believe you're looking to have an instance evaluate as true/false based on the outcome of validation.
While some languages allow you to override the boolean value of an instance, php does not (except for casting to string, that is. See PHP's Magic Methods).
Also, the booleans page in the PHP Manual has a list of things that evaluate to false, but it doesn't give any method of overriding the behavior either.
That being said, I'd suggest going with JRL's idea, and construct a chain of validation rules, then 'execute' it with a function that returns the boolean needed in your if statement.
You could wrap them up in a subclass, perhaps.
e.g. if you have
class Validate {
public function checkSomething($data) {
if ($data === $valid) {
return true;
}
return false;
}
public function checkSomethingElse($data) {
if ($data === $valid) {
return true;
}
return false;
}
}
You could do this:
class ValidateChain extends Validate {
protected $valid = true;
public function checkSomething($data) {
if (false === parent::checkSomething($data)) {
$this->valid = false;
}
return $this;
}
public function checkSomethingElse($data) {
if (false === parent::checkSomethingElse($data)) {
$this->valid = false;
}
return $this;
}
public function getIsValid() {
return $this->valid;
}
}
$v = new ValidationChain();
$valid = $v->checkSomething()->checkSomethingElse()->getIsValid();
Quick and dirty, E&OE. And you'd probably need to add a way to find out which bits weren't valid, etc.

Categories