Force what is being given as a string parameter in php - php

I've searched for the answer to my problem, but I'm having trouble putting it to words. So... I'm asking here.
I have a method which is created to echo text with a given string and an error type:
public function output($string, $errorType) : void
{
echo "<div class='alert alert-$errorType' role='alert'>$string</div>";
}
Since I'm using bootstrap, this $errorType should always be warning, danger or success. But since it's a string, I can give $errorType an entirely different value. I want to force the usage of the three error types: warning, danger or success. What is the best way to do this?

This question is one of input validation.
In your context, there's several approaches you can take. The ultimate goal is to check if the input is in a list of accepted values, but the main question is where, when and how to do this.
The most intuitive approach would be to put it right inside the method, eg
if (!in_array($input, self::ACCEPTABLE_INPUT)) {
throw some exception
}
An alternative mechanism in the same spirit would be to use assert:
assert(in_array($input, self::ACCEPTABLE_INPUT));
However this assumes a decent test coverage.
One way which I generally tend to prefer is to model the error type as a value object, eg.
final class ErrorType
{
private const ACCEPTED = ['warning', 'danger', 'success'];
private $type;
public function __construct(string $type)
{
if (!in_array($type, ErrorType::ACCEPTED) {
throw some exception
}
$this->type = $type;
}
public function __toString(): string
{
return $this->type;
}
}
It's a bit more code, but allows you to follow the Single Responsibility Principle.
Your function would simply look like this:
public function output($string, ErrorType $errorType) : void
{
echo "<div class='alert alert-$errorType' role='alert'>$string</div>";
}

Throw an Exception if input value of $errorType is not a valid value. You can do something like as below:
public function output($string, $errorType) : void
{
// define all possible valid input values for $errorType
$accepted_error_type = array('warning', 'danger', 'success');
// throw exception if invalid input
if (!in_array($errorType, $accepted_error_type) {
throw new Exception('Invalid errorType in function output. Expected: warning, danger or success. Received: ' . $errorType);
}
// Rest of the implementation details come here
echo "<div class='alert alert-$errorType' role='alert'>$string</div>";
}

You can let users to give int as $errorType. For example 0 for warning, 1 for danger, 2 for success. For other values, you can either set a default value (for example warning) or you can return exception.
public function output($string, $errorType)
{
$type = "";
if($errorType == 0)
$type = "warning";
else if($errorType == 1)
$type = "danger";
else if($errorType == 2)
$type = "success";
else
{
// You can set a default error type here, if input is incorrect
// Or you can simply return an exception to inform user about limitations.
}
echo "<div class='alert alert-$type' role='alert'>$string</div>";
}

Related

How to check all if using php?

I have a Validation class and a failure method which takes 2 strings as parameters. My problem is that I have to go through all the ifs and finally put as parameters for this function, which fields have passed the validation and display the appropriate message. With the current code, unfortunately, it only displays a message for the last field that did not pass validation, and it should for all that did not pass. I will be grateful for your help.
class Validation
{
private function __construct(private array $fieldsAndMessages)
{
}
public static function failure(string $fieldName, string $message): Validation
{
return new self([$fieldName => $message]);
}
}
$field = '';
$message = '';
if ($request->settingsSubmit()) {
if ($request->wantsSubmitPhoto()) {
$extensionPhoto = new PhotoExtension($request->photo());
if (!in_array($extensionPhoto->getExtension(), ['jpg', 'png', 'jpeg'])) {
$field = 'photo';
$message = 'Invalid photo';
}
}
if ($request->wantsSubmitAvatar()) {
$extensionAvatar = new AvatarExtension($request->avatar());
if (!in_array($extensionAvatar->getExtension(), ['jpg', 'png', 'jpeg'])) {
$field .= 'avatar';
$message .= 'Invalid avatar';
}
return new SettingsView($userId, Validation::failure($field, $message));
UPDATE
as the OP has mentioned that he cannot modify the failure method (which should've done in the question itself), I will provide a potentially possible workaround.
The idea is to keep the Validation class as is without modifying it and instead you may have an array that will hold instances of the Validation class where each error found will create a new instance of Validation class.
/** this variable will hold all the potential errors where each error is an instance of Validation class */
$errors = [];
if ($request->settingsSubmit()) {
if ($request->wantsSubmitPhoto()) {
$extensionPhoto = new PhotoExtension($request->photo());
if (!in_array($extensionPhoto->getExtension(), ['jpg', 'png', 'jpeg']))
$errors[] = Validation::failure('photo', 'Invalid photo');
}
if ($request->wantsSubmitAvatar()) {
$extensionAvatar = new AvatarExtension($request->avatar());
if (!in_array($extensionAvatar->getExtension(), ['jpg', 'png', 'jpeg']))
$errors[] = Validation::failure('avatar', 'Invalid avatar');
}
}
/**
* pass the array ($errors) containing the errors to the SettingsView class.
* the "SettingsView" class should expect an array of `Validation` class as the second parameter.
*/
return new SettingsView($userId, $errors);
Original Answer
With your current implementation, you keep on overriding the $field and $message variables and you'll always have 0 or 1 error.
To allow having more than one error, you may tweak your Validation class' failure method and have it accept an array of error messages instead of a expecting a field and a message which will allow you to display all the found errors when your validation process fails.
The errors array that the failure method expects should have the following structure: the keys are the field name and the values are the actual messages (not required, I based that upon the usage of field names in your code and because you try to store the names of the fields having errors).
Here's an example of an error messages array that you may now pass to your method:
$errors = [
/** the keys are the fields | the values are the messages */
'photo' => 'Invalid photo',
'avatar' => 'Invalid avatar',
];
Your failure method could be refactored as the following to accept an array of error messages:
/** accepts an array of error messages where the keys act as the field names and the values as the actual error messages for the fields */
public static function failure(array $errors): self
{
/** pass the received array to the constructor */
return new self($errors);
}
An example of usage that is based on your validation flow could be as follows:
/** this variable will hold all the potential errors */
$errors = [];
if ($request->settingsSubmit()) {
if ($request->wantsSubmitPhoto()) {
$extensionPhoto = new PhotoExtension($request->photo());
if (!in_array($extensionPhoto->getExtension(), ['jpg', 'png', 'jpeg']))
$errors['photo'] = 'Invalid photo';
}
if ($request->wantsSubmitAvatar()) {
$extensionAvatar = new AvatarExtension($request->avatar());
if (!in_array($extensionAvatar->getExtension(), ['jpg', 'png', 'jpeg']))
$errors['avatar'] = 'Invalid avatar';
}
}
/** pass the array ($errors) containing the errors to the failure method */
return new SettingsView($userId, Validation::failure($errors));
Now your Validation class should have the errors in an array which you may later loop through those errors and display them for the user.
Disclaimer: The above code examples are not expected to work as is as they only meant to showcase the answer's idea and to demonstrate it. I recommend you take the idea from them and the above logic/explanations above and build upon. Also, that's one way of doing the things and it's definitely not the only possible way to get you on track.

PHP returns object as null occasionally and unintentionally

We have PHP code in production that sometimes fails with "Call to member function on null", although the same code path executes fine several times before that in one invocation. We have a test that reproduces the error consistently at the same run of the loop.
I already proved that the object gets created correctly in the factory even if it gets returned as null. The factory method must not return null in any case, as indicated in the DocBlock. This question is not related to nullable return types or something like that.
The process does not exceed memory or runtime limitations and I already tried turning off the garbage collector, but no luck. The error happens both in PHP 7.0 and 7.3 on Debian, did not try on other versions or operating systems.
I am not allowed to paste the real code here, but I wrote a simple mockup to explain in more detail. Please keep in mind that this demo code will not result in the error, it is just meant to show the general structure of the program that runs into this fault.
// Three placeholder classes with common methods
class Bender
{
public function common()
{
echo "Bend, bend!" . PHP_EOL;
}
}
class Clamper
{
public function common()
{
echo "Clamp, clamp!" . PHP_EOL;
}
}
class Worker
{
public function common()
{
echo "Work, work!" . PHP_EOL;
}
}
// abstract class with static factory to produce objects
abstract class MomCorp
{
/**
* Factory to create one of several objects
*
* #param string $name
* #return Bender|Clamper|Worker
*/
public static function factory($name)
{
$type = self::managementDecision($name);
switch ($type)
{
case "bender":
$robot = new Bender();
break;
case "clamper":
$robot = new Clamper();
break;
default:
$robot = new Worker();
}
// optional QA works flawlessly here, object is fine all the time!
// $robot->common();
return $robot;
}
public static function managementDecision($name)
{
// irrelevant magic happens on $name here
return "bender";
}
}
foreach (['Rodriguez', 'Angle-ine', 'Flexo', 'Billie'] as $newName)
{
echo "$newName: ";
// The next two lines break after some loops - why?
// The perfectly functional object in factory gets returned as null
$bot = MomCorp::factory($newName);
$bot->common();
}
// SAMPLE OUTPUT
// Rodriguez: Bend, bend!
// Angle-ine: Bend, bend!
// Flexo: Bend, bend!
// Billie: Call to a member function common() on null
Has anyone experienced the same and has any hints on what might cause such an error and how to fix it?

Best way to handle static text / messages in PHP OOP project (JSON maybe?)

Until now, unless I made a multilingual website (where I would use .mo & .po files), all the text would be scrambled all around the template and / or class files. Instead, I would like to store all static text in a file that is easily editable by my coworkers and clients (that rules out database storage and POedit).
I made a JSON file that stores the messages / static text like this:
{
"titles": {
"main_title": "This is the main title of the website",
"login_page_title": "Please, sing in",
"about_page_title": "About us"
},
"errors": {
"empty_required_field": "This field is required.",
"database_connection_error": "Couldn't connect to the database.",
}
}
Then I import it in the index.php file:
$messages = json_decode(file_get_contents("messages.json"));
And use it like:
echo($messages->titles->main_title);
Which has been working so far so good (although I'm uncertain that there aren't better ways to archieve this). At least in the template pages where everything is html with minimal logic.
But I'm having trouble using the strings from the JSON file inside the classes' functions. I would like to use the error messages when throwing exceptions, for example. But I'm quite reluctant about stating "global $message" in every function where it's used (feels repetitive). Also everybody says that globals are naughty.
So my questions are two:
1) Is the JSON file a good way to handle my problem? (and if not, why, and which method would be better?).
2) How could I retrieve the stored strings from inside the classes? I'm thinking something like extending the Exception class to include the error messages, but I'm unsure of how to do it.
Thanks in advance for your help.
One approach, which Laravel takes, is creating some sort of directory tree like the following:
lang/
en/
titles.php
errors.php
titles.php could contain the following:
<?php
return [
'main_title' => 'This is the main title of the website',
'login_page_title' => 'Please, sing in',
'about_page_title' => 'About us'
];
As for errors.php:
<?php
return [
'empty_required_field' => 'This field is required.',
'database_connection_error' => "Couldn't connect to the database.",
];
I don't really like the JSON approach because it's not very flexible. For one, in PHP files, you have access to any variables you may want to give it, there's comments, possibility of using functions to create some messages, etc. This is why I recommend the above method.
In order to get the messages, you would require the file in a variable, like $titles = require 'lang/en/titles.php', using it like: $titles['main_title']. This method also makes it easy to change the language if needed.
While I'm not 100% sure I understand your exception problem, you would throw an exception with the appropriate message like: throw new Exception($errors['empty_required_field']);
In the end I opted for a Singleton class that loads/includes a separate text file. Nice global scope and should be easy to adapt to other needs (multilingüal, separate language files, or whatever). As I said I'm no expert so all critique is welcome.
<?php
class CustomText {
private static $instance = null;
private static $text;
private function __clone() {}
// On construct, checks if the strings are stored in a session.
// If not, retrieves them from file and stores them in a session.
private function __construct() {
if(self::isStoredInSession() == true) {
self::$text = $_SESSION["custom_text"];
} else {
//self::$text = json_decode(file_get_contents("messages.json"),true);
self::$text = include_once("messages.php");
self::saveToSession();
}
}
// Private initialization called on every public method so I don't have to worry about it on other files.
private static function initialize() {
if(self::$instance == null) {
self::$instance = new self;
}
}
// Session management
private static function saveToSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION["custom_text"])) {
$_SESSION["custom_text"] = self::$text;
}
}
private static function isStoredInSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(isset($_SESSION["custom_text"])) {
return true;
}
return false;
}
// Sample public functions
public static function getText($section,$string){
self::initialize();
if(isset(self::$text[$section][$string])) {
return self::$text[$section][$string];
} else {
return "";
}
}
public static function getError($string) {
self::initialize();
if(isset(self::$text["error"][$string])) {
return self::$text["error"][$string];
} else {
return "";
}
}
public static function getWebsiteTitle($section,$divider = " - ") {
self::initialize();
$title = "";
if(isset(self::$text["title"]["main"])) {
$title .= self::$text["title"]["main"];
}
if(isset(self::$text["title"][$section])) {
if(!empty($title)) {
$title .= $divider;
}
$title .= self::$text["title"][$section];
}
return $title;
}
}
What worries me the most is that I'm not sure that storing the data in a session is better that including a file on each page, and I have everything twice in the session variable and the class parameter.

Can We Restrict PHP Variables to accept only certain type of values

i am wondering is it possible to restrict php variable to accept only certain type of variable it is defined to accept.
e-g if we see in C#
public int variable = 20;
public string variable2 = "Hello World";
so what would it be like in php if i want to restrict type for variable.
public int $variable = 20;
so that if i try to assign string to integer variable i get the error.
public int $varaible = "Hello World"; //As type is integer, it should throw error.
is there such thing defining types in PHP for variables before assigning values to it??
TL;DR Not directly, no. PHP is not strictly-typed. There are, however, a few workarounds that may work for you in the context of function parameters or properties of classes.
Long answer: PHP is not a strictly-typed language, but loosely-typed. You can give a variable any value you want, regardless of how it was initialized. So, you can't simply type something like int $myVar and expect $myVar = "foo"; to throw an error. But PHP does offer a few handy features to get you to the same end when dealing with function parameters or properties of a class.
Option 1: Type hints
You can use a "type hint" for function parameters:
class SomeClass
{
/* code here */
}
function foo(SomeClass $data)
{
/* code here */
}
foo() will only accept parameters of type SomeClass. Passing it, say, an int will throw a fatal error. This doesn't work in PHP < 7 if the parameters are intended to be base types, like int, string, etc., so you can't do function foo(int $data) {...}. That said, there are a few libraries out there that attempt to force it to work at the expense of a little speed. Also, PHP 7 adds a lot of support for this kind of thing, as does the Hack language based on PHP.
Pros:
Easy
Intuitive
Cons:
Only works for program-defined classes
Unavailable for base types
Option 2: Getters and Setters
You can also use getters and setters, like so:
class SomeClass
{
private $foo = 0;
function setFoo($val = 0)
{
// force it to be an int
if (is_integer($val) {
$this->foo = $val;
} else {
// throw an error, raise an exception, or otherwise respond
}
}
}
Pros:
Relatively easy
Relatively intuitive
Cons:
Only works in program-defined classes
Unavailable for base types
Requires lots of code
Option 3: Magic Methods
This method is my favorite, but also the most complicated. Use the __set() magic method to deal with class properties.
class MyClass {
private $type = 0; // we will force this to be an int
private $string = ''; // we will force this to be a string
private $arr = array(); // we will force this to be an array
private $percent = 0; // we will force this to be a float in the range 0..100
function __set($name, $value) {
switch ($name) {
case "type":
$valid = is_integer($value);
break;
case "string":
$valid = is_string($value);
break;
case "arr":
$valid = is_array($value);
break;
case "percent":
$valid = is_float($value) && $value >= 0 && $value <= 100;
break;
default:
$valid = true; // allow all other attempts to set values (or make this false to deny them)
}
if ($valid) {
$this->{$name} = $value;
// just for demonstration
echo "pass: Set \$this->$name = ";
var_dump($value);
} else {
// throw an error, raise an exception, or otherwise respond
// just for demonstration
echo "FAIL: Cannot set \$this->$name = ";
var_dump($value);
}
}
}
$myObject = new MyClass();
$myObject->type = 1; // okay
$myObject->type = "123"; // fail
$myObject->string = 1; // fail
$myObject->string = "123"; // okay
$myObject->arr = 1; // fail
$myObject->arr = "123"; // fail
$myObject->arr = array("123"); // okay
$myObject->percent = 25.6; // okay
$myObject->percent = "123"; // fail
$myObject->percent = array("123"); // fail
$myObject->percent = 123456; // fail
Pros:
Relatively easy
Intuitive
Extremely powerful: one setter to rule them all
Cons:
Only works in program-defined classes
Unavailable for base types
Requires lots of switching or if/else logic
Can cause problems with IDEs not auto-completing property types correctly
Here's a demo of this approach.
Closing Thoughts
Finally, if you're using an IDE like PHPStorm, don't forget about PHPDoc type hints:
/* #var integer */
$foo = 0; // will result in warnings if the IDE is configured properly and you try to do something like substr($foo, 1, 4);
And if you really want to go hard core, you can do strong typing using Hack, at the expense of making your code less portable and less compatible (for now) with major IDEs.
Of course, none of these is a substitute for explicitly validating user input and thoroughly testing the application's response to unexpected input types.
No. PHP is not a strictly typed language. You can however use type hints in functions and methods.
If class or interface is specified as type hint then all its children or implementations are allowed too.
Type hints can not be used with scalar types such as int or string. Resources and Traits are not allowed either.
The Scalar types being:
string
bool
int
float
Examples:
function (array $theArr) {
// body
}
class X {
public function __construct(SomeOtherClass $arg) {
// body
}
public function setFoo(Foo $foo) {
}
}
See the manual for more specifics: http://php.net/manual/en/language.oop5.typehinting.php
You have to made it by your own hands, example :
function setInt(&$var, $value) {
if(!is_integer($value) {
throw new Exception("Integer wanted " . gettype($value) . " received");
}
$var = $value;
}

Is it possible to make an object return false by default?

I tried to ask this before, and messed up the question, so I'll try again. Is it possible to make an object return false by default when put in an if statement? What I want:
$dog = new DogObject();
if($dog)
{
return "This is bad;"
}
else
{
return "Excellent! $dog was false!"
}
Is there a way this is possible? It's not completely necessary, but would save me some lines of code. thanks!
No, PHP has no support for operator overloading. Maybe they'll add it in a future version.
Use the instanceof keyword.
For example
$result = Users->insertNewUser();
if($result instanceof MyErrorClass){
(CHECK WHAT WENT WRONG AND SAY WHY)
} else {
//Go on about our business because everything worked.
}
Info is here.
Use this? Not a real neat solution, but does what you want:
<?php
class Foo
{
private $valid = false;
public function Bar ( )
{
// Do stuff
}
public function __toString ( )
{
return ( $this -> valid ) ? '1' : '0';
}
}
?>
Zero is considered false, one is considered true by PHP
I was attempting to do this myself and found a solution that appears to work.
In response to the others who were trying to answer the question by telling the asker to use a different solution, I will also try to explain the reason for the question. Neither the original poster or I want to use an exception, because the point is not to use exception handling features and put that burden on any code we use this class in. The point, at least for me, was to be able to use this class seamlessly in other PHP code that may be written in a non-object-oriented or non-exception-based style. Many built-in PHP functions are written in such a way that a result of false for unsuccessful processes is desirable. At the same time, we might want to be able to handle this object in a special way in our own code.
For example, we might want to do something like:
if ( !($goodObject = ObjectFactory::getObject($objectType)) ) {
// if $objectType was not something ObjectFactory could handle, it
// might return a Special Case object such as FalseObject below
// (see Patterns of Enterprise Application Architecture)
// in order to indicate something went wrong.
// (Because it is easy to do it this way.)
//
// FalseObject could have methods for displaying error information.
}
Here's a very simple implementation.
class FalseObject {
public function __toString() {
// return an empty string that in PHP evaluates to false
return '';
}
}
$false = new FalseObject();
if ( $false ) {
print $false . ' is false.';
} else {
print $false . ' is true.';
}
print '<br />';
if ( !$false ) {
print $false . ' is really true.';
} else {
print $false . ' is really false.';
}
// I am printing $false just to make sure nothing unexpected is happening.
The output is:
is false.
is really false.
I've tested this and it works even if you have some declared variables inside the class, such as:
class FalseObject {
const flag = true;
public $message = 'a message';
public function __toString() {
return '';
}
}
A slightly more interesting implementation might be:
class FalseException extends Exception {
final public function __toString() {
return '';
}
}
class CustomException extends FalseException { }
$false = new CustomException('Something went wrong.');
Using the same test code as before, $false evaluates to false.
I recently had to do something similar, using the null object pattern. Unfortunately, the null object was returning true and the variable in question was sometimes an actual null value (from the function's default parameter). The best way I came up with was if((string)$var) { although this wouldn't work for empty arrays.
Putting something in "an if statement" is simply evaluating the variable there as a boolean.
In your example, $dog would need to be always false for that to work. There is no way to tell when your variable is about to be evaluated in a boolean expression.
What is your ultimate purpose here? What lines of code are you trying to save?
I'm not sure about the object itself. Possible. You could try something like, add a public property to the DogObject class and then have that set by default to false. Such as.
class DogObject
{
var $isValid = false;
public function IsValid()
{
return $isValid;
}
}
And then when you would instantiate it, it would be false by default.
$dog = new DogObject();
if($dog->IsValid())
{
return "This is bad;"
}
else
{
return "Excellent! $dog was false!"
}
Just a thought.
If I understand what your asking, I think you want to do this:
if (!$dog){
return "$dog was false";
}
The ! means not. SO you could read that, "If not dog, or if dog is NOT true"
Under what conditions do you want if($dog) to evaluate to false? You can't do what you've literally asked for, but perhaps the conditioned could be replaced by something that does what you want.
class UserController
{
public function newuserAction()
{
$userModel = new UserModel();
if ($userModel->insertUser()) {
// Success!
} else {
die($userModel->getError());
}
}
}
Or
class UserController
{
public function newuserAction()
{
$userModel = new UserModel();
try {
$userModel->insertUser()
}
catch (Exception $e) {
die($e);
}
}
}
There are a million ways to handle errors. It all depends on the complexity of the error and the amount of recovery options.
How about using an Implicit Cast Operator like the following C# ?
like so:
class DogObject
{
public static implicit operator bool(DogObject a)
{
return false;
}
}
Then you can go...
var dog = new DogObject();
if(!dog)
{
Console.WriteLine("dog was false");
}

Categories