I want to get the value of an Enum in PHP by its name.
My enum is like:
enum Status : int
{
case ACTIVE = 1;
case REVIEWED = 2;
// ...
}
Status::from(2) can be used to get "REVIEWED", but how can I resolve the value from the name stored in a string ?
Well, it seems there is not any built-in solution in PHP. I've solve this with a custom function:
enum Status : int
{
case ACTIVE = 1;
case REVIEWED = 2;
// ...
public static function fromName(string $name): string
{
foreach (self::cases() as $status) {
if( $name === $status->name ){
return $status->value;
}
}
throw new \ValueError("$name is not a valid backing value for enum " . self::class );
}
}
Then, I simply use Status::fromName('ACTIVE') and get 1
If you want to mimic the from and tryFrom enum functions, you can also add:
public static function tryFromName(string $name): string|null
{
try {
return self::fromName($name);
} catch (\ValueError $error) {
return null;
}
}
You can use reflection for Backed case:
$reflection = new ReflectionEnumBackedCase(Status::class, 'REVIEWED');
$reflection->getBackingValue(); // 2
$reflection->getValue() // Status::REVIEWED if you need case object
Or enum reflection:
$reflection = new ReflectionEnum(Status::class);
$reflection->getCase('REVIEWED')->getValue()->value // 2
see also ReflectionEnumUnitCase
To get value from the name:
enum Status : int
{
case ACTIVE = 1;
case REVIEWED = 2;
// ...
}
print(Status::REVIEWED->value);
Enum documentation
I use a custom method too, but I return an enum.
The from method returns an enum, not a value. I think the fromName method should return an enum too. Then you have access to all methods of the enum. You don't need use another method - from.
public static function fromName(string $name): self
{
foreach (self::cases() as $status) {
if( $name === $status->name ){
return $status;
}
}
throw new \ValueError("$name is not a valid backing value for enum " . self::class );
}
public static function tryFromName(string $name): self|null
{
try {
return self::fromName($name);
} catch (\ValueError $error) {
return null;
}
}
The constant() function can return the value of a constant using a string variable.
This also applies to Enum class constants as mentioned in the basic enumeration section of the PHP Manual.
$name = 'REVIEWED';
$status = constant("Status::{$name}");
If anyone cares, I have created a library around that.
https://github.com/henzeb/enumhancer
Just add the trait to your enum
enum Status : int
{
use Henzeb\Enumhancer\Enhancers;
case ACTIVE = 1;
case REVIEWED = 2;
// ...
}
Status::get('ACTIVE');
Status::tryGet('ACTIVE');
You then don't have to use values at all. In that case you can also simply use from.
enum Status
{
use Henzeb\Enumhancer\Enhancers;
case ACTIVE;
case REVIEWED;
// ...
}
Status::from('ACTIVE')->key(); // returns 0
Status::tryFrom('Reviewed')->key(); // returns 1
It does so much more than that, but it defeats copying and pasting snippets around.
Related
In PHP 8.1, BackedEnum offer a from and tryFrom method to get an enum from a value. How can the same be achieved by non backed enums?
Example BackedEnum:
enum MainType: string
{
case Full = 'a';
case Major = 'b';
case Minor = 'c';
}
var_dump(MainType::tryFrom('a')); // MainType::Full
var_dump(MainType::tryFrom('d')); // null
However this doesn't exist for regular enums.
How would I retrieve a "normal" Enum by name, like:
enum MainType
{
case Full;
case Major;
case Minor;
}
$name = (MainType::Full)->name
var_dump(name); // (string) Full
One option I've found is to simply add a tryFromName function, accepting a string and looping over all the cases like this:
enum MainType
{
case Full;
case Major;
case Minor;
public static function tryFromName(string $name): ?static
{
foreach (static::cases() as $case) {
if ($case->name === $name) {
return $case;
}
}
return null;
}
}
$name = (MainType::Full)->name
var_dump(name); // (string) Full
var_dump(MainType::tryFromName($name)); // MainType::Full
This works, however it seams counter intuitive to enable a foreach loop going over all possibilities just to create an enum.
Therefore the question is, what is the right way to get an Enum in PHP from the name.
You can use Reflection:
trait Enum {
public static function tryFromName(string $name): ?static
{
$reflection = new ReflectionEnum(static::class);
return $reflection->hasCase($name)
? $reflection->getCase($name)->getValue()
: null;
}
}
enum Foo {
use Enum;
case ONE;
case TWO;
}
var_dump( Foo::tryFromName('TWO') ); // enum(Foo::TWO)
var_dump( Foo::tryFromName('THREE') ); // null
Works also for Backed Enums.
My two cents: My package https://github.com/henzeb/enumhancer does that for you, including several other things that might be useful.
I'd like to get numbers as the real value while importing a file, e.g:
When I open the csv, cell value: 198610012009011005
But when I import that using Laravel Excel, it'll be formatted to 1.98610012009011E+17
How can I get the real value of the number (198610012009011005) ?
I tried bellow code but it didn't work
$data['excel'] = Excel::load($path, function ($reader) {
$reader->sheet(0, function ($sheet) {
$sheet->setColumnFormat(["A" => "#"]);
});
})->toArray();
Actually the value you get is true. 1.98610012009011E+17 is the form of exponential value. But if you want get it as string form try this approach.
You should create a ValueBinder class.
// MyValueBinderClass
use PHPExcel_Cell;
use PHPExcel_Cell_DataType;
use PHPExcel_Cell_IValueBinder;
use PHPExcel_Cell_DefaultValueBinder;
class MyValueBinder extends PHPExcel_Cell_DefaultValueBinder implements PHPExcel_Cell_IValueBinder
{
public function bindValue(PHPExcel_Cell $cell, $value = null)
{
if (is_numeric($value))
{
$cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
return true;
}
// else return default behavior
return parent::bindValue($cell, $value);
}
}
And then bind it to while you load the csv file:
$myValueBinder = new MyValueBinder;
$data = Excel::setValueBinder($myValueBinder)
->load($path)->toArray();
reference: http://www.maatwebsite.nl/laravel-excel/docs/import#formatting
It's out of the range for integer type .
use string for column type in DB
or cast it
$number= (float) $value;;
How $mysettings can be true while we are initializing it with null? is this a method to prevent SQL injection? It would be appreciated if you could explain the code below.
public function __construct($mysettings = null)
{
$this->shop_version = Mage::getVersion();
$this->moduleversion = Mage::getConfig()->getModuleConfig('Messagemodule')->version;
$this->apppid = Mage::getStoreConfig('magemessage/appId');
if (empty($this->apppid)) {
$this->apppid = 'no-appId';
}
$this->connectortype = ($settingvariable = Mage::getStoreConfig('Messagemodule/magemessage/connector', 0)) ? $settingvariable : 'auto';
if ($mysettings) {
$this->connectortype = $mysettings;
}
}
When you specify a default value in a PHP method (including a constructor), that's all it is - a default.
So if you have
class Foo {
public function __construct($mysettings = null) {
...
}
}
then you are providing two ways of constructing the class. You can either call
$foo = new Foo();
with no arguments, in which case $mysettings will be initialised to null. Or you can call
$settings = array('key' => 'value');
$foo = new Foo($settings);
in which case the $settings array will be passed into the new instance. The benefit this provides is that you don't need to provide an empty array to new instances for which you don't need custom settings; you can just omit the argument.
The check if ($mysettings)... in the class ensures that the settings are only used if they are provided - a PHP if statement can operate on lots of different types, not just booleans. In this case, if the variable is null, the condition will evaluate to false.
Have a look at this code:
<?php
function required($something)
{
echo $something;
}
required();
It throws a fatal error, because $something was required, but not passed. https://3v4l.org/fIKB9
Now look here:
<?php
function required($something = 'hello')
{
echo $something;
}
required();
required(' R.Toward');
Which outputs Hello R.Toward https://3v4l.org/nQF8r
So in essence, it is a way of setting a default optional value.
I have a bunch of optional settings and I'm sick of checking for isset and property_exists.
In Laravel, if I ask for a property that does not exist on a model or request, I get null and no complaints (errors). How can I do the same for my data structure.
If I try array, I can't do simple $settings['setting13'], I have to either pre-fill it all with nulls or do isset($settings['setting13']) ? $settings['setting13'] : '' or $settings['setting13'] ?? null. If I try an object (new \stdClass()), $settings->setting13 still gives me a warning of Undefined property.
How can I make a class such that it responds null or an empty string whenever it is asked for a property that it doesn't have?
Simply do what Laravel does, create a class that deals with your data structure which returns a value if key exists, and something else if it doesn't.
I'll illustrate with an example class (this class supports the "dot notation" of accessing array keys):
class MyConfigClass
{
protected $data;
public function __construct(array $data)
{
$this->data = $data;
}
public function get($path = '', $default = null)
{
if(!is_string($path))
{
return $default;
}
// There's a dot in the path, traverse the array
if(false !== strpos('.', $path))
{
// Find the segments delimited by dot
$segments = explode('.', $path);
$result = $this->data;
foreach($segments as $segment)
{
if(isset($result[$segment]))
{
// We have the segment
$result = $result[$segment];
}
else
{
// The segment isn't there, return default value
return $default;
}
}
return $result;
}
// The above didn't yield a result, check if the key exists in the array and if not - return default
return isset($this->data[$path]) ? $this->data[$path] : $default;
}
}
Use:
$my_structure = [
'url' => 'www.stackoverflow.com',
'questions' => [
'title' => 'this is test title'
]
];
$config = new MyConfigClass($my_structure);
echo $config->get('url'); // echoes www.stackoverflow.com
echo $config->get('questions.title'); // echoes this is test title
echo $config->get('bad key that is not there'); // returns null
There is also a possibility to create wrapper as Jon Stirling mentioned in a comments. This approach will allow to keep code clean and also add functionality via inheritance.
<?php
class myArray implements ArrayAccess {
private $container;
function __construct($myArray){
$this->container = $myArray;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$settings = array("setting1"=>1,"setting2"=>2,"setting3"=>3);
$arr = new myArray($settings);
echo $arr['setting1'];
echo "<br>";
echo $arr['setting3'];
echo "<br>";
echo $arr['setting2'];
echo "<br>";
echo "------";
echo "<br>";
echo $arr['setting4'] ?:"Value is null";
!empty($settings['setting13']) ? $settings['setting13'] : ''
can be replaced with
$settings['setting13'] ?: ''
as long as whatever you want to print and whatever you want to check exists is the same expression. It's not the cleanest thing ever - which would be to check the existence of anything - but it's reasonably clear and can be chained :
echo ($a ?: $b ?: $c ? $default ?: '');
However, you are not the first who are "sick of checking for isset and property_exists, it's just that we still have to do it, or else we get unexpected results when we expect it the least.
It's not about saving time typing code, it's about saving time not debugging.
EDIT : As pointed in the comments, I wrote the first line with isset() instead of !empty(). Since ?: returns the left operand if it's equal to true, it's of course uncompatible with unchecked variables, you have at least to check for existence beforehand. It's emptiness that can be tested.
The operator that returns its left operand if it exists and is different from NULL is ??, which can be chained the same way ?: does.
Admittedly not the best way to do this, but you can use the error suppressor in php like this:
$value = #$settings['setting13'];
This will quitely set$value to NULL if $settings['setting13'] is not set and not report the undefined variable notice.
As for objects, you should just calling for attributes that are not defined in class.
Is there a way to allow enum or const as method argument in PHP. In Qt/C++ you could use it like this, but ofcourse C++ supports that (language dependant).
Qt/C++/SslSocket
enum PeerVerifyMode {
VerifyNone,
QueryPeer,
VerifyPeer,
AutoVerifyPeer
};
void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode);
I PHP I tried this:
First:
class Controller_My
{
const MENU_FRONT = 0;
const MENU_SESSION = 1;
public function render($menu_model)
{
$menu_model = intval($menu_model);
if( $menu_model === 0 )
$menu = new Model_Menu_Front();
if( $menu_model === 1 )
$menu = new Model_Menu_Session();
}
}
I've read also this post how to use constant from class as an argument definition in php function?. But even with http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism interface/implements solution if you are paranoined you use switch/if statement. Like this:
class EmployeeType...
int payAmount(Employee emp) {
switch (getTypeCode()) {
case ENGINEER:
return emp.getMonthlySalary();
case SALESMAN:
return emp.getMonthlySalary() + emp.getCommission();
case MANAGER:
return emp.getMonthlySalary() + emp.getBonus();
default:
throw new RuntimeException("Incorrect Employee");
}
}
Take a look at SplEnum and the user notes for an example.