I'm looking at enum in PHP 8.1. I have a string variable gathered from a database record, and I want to select the correct enum value case based on that string. For example:
enum Test: string {
case FOO = 'baz';
case BAR = 'buz';
}
I can select the first of those using the string value of the case:
$x = 'baz';
$y = Test::from($x);
// $y is now `Test::FOO`.
But what about the other way around? What if $x = "FOO"? How do I select the enum case from "FOO"?
Hard-coded, it would be Test::FOO, but I don't know the syntax for using a variable. I've tried a few things like Test::{$x} but the enum syntax doesn't seem to like variables very much.
The standard example all the tutorials give seems a perfect use case for this usage, but none of them mention it.
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
I can totally imagine needing to get ♣ from "Clubs". I can't imagine ever needing the reverse.
To me, storing the name of the constant and not the value defeats the purpose of a backed enum. But, reading through many of the discussions on enums, many people were against backed enums and/or stringified enums in general, so I won't argue the point except to say that I would consider it the same as storing Clubs for a class constant such as:
class Suit
{
const Clubs = '♣';
const Diamonds = '♦';
const Hearts = '♥';
const Spades = '♠';
}
To your question, yes you can get the enum back by using the constant function (as noted in this thread):
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
$cardString = 'Clubs';
$cardEnumFQString = Suit::class . '::' . $cardString;
$cardEnum = constant($cardEnumFQString);
echo $cardEnum->value;
Demo here: https://3v4l.org/WTrIv#v8.1.10
Personally, when I use backed enums it is for database, URL transport or similar, and I use the value. If I want to expose anything user-facing I more often than not just add a custom attribute and I have a common utility function that parses that.
EDIT Here's some helper code that shows a trait I use to reflect on enum attributes: https://3v4l.org/QKBvU#v8.1.10. It might seem like overkill for some people, but for some objects I like to keep their meta information attached to the object instead of a separate render file.
EDIT: Enums vs Class Constants
This is not a definitive answer to the question in any way, I want to make that clear. There is definitely a lot more that I'm not covering and I'd encourage a read-through on the original discussion as well as the follow-up discussion and the over-arching ADTs RFC. There's a lot in those discussions, pros, cons, WTFs, alternatives, "I don't get it", etc.
One of the major differences between an enum and a class constant is that an enum is a first-class object in PHP and as such it can have methods, whereas a class constant is limited to scalars, scalar expressions and arrays (ignoring recent post-enum changes). This means you can pass an object that holds a specific value around, and have that object include methods. The PHP documentation has a great example for the color method on Suit.
You could define your constants in one file and include a utility class with methods like getColor(string $colorName) however there's no way to limit the choices that get passed to it, nor can you guard against someone using a literal instead of your constant.
Can you do that with pure classes? Absolutely, and many of us used libraries such as https://github.com/myclabs/php-enum or https://github.com/spatie/enum to do that. This just unifies it at the language level.
I definitely agree that it can be a grey area. I came from a .Net background originally so I've had enums for a very long time, and even wrote a "why enums vs static class members" over a decade ago, so for me I've had a long time to play with them, and was very excited when they came to PHP. The best I can tell you is that you might just have to play with them to see if they fit. I'm very certain that some long-time PHP people won't use enums ever because they've solved this problem in the past in other ways, and I think that's okay, too.
Related
I have many session vars. Should I use this
$_SESSION[SessionHelper::ROUTING] = SessionHelper::MODULE_A;
class SessionHelper {
const ROUTING = 'SessionHelper.routing';
const MODULE_A = 1;
const MODULE_B = 2;
}
or this?
$_SESSION['routing'] = 1;
The first seems to be maintenanable but hard to read in some case. For example:
if(isset($_SESSION[SessionHelper::ROUTING]) &&
$_SESSION[SessionHelper::ROUTING] = SessionHelper::MODULE_A) {
....
The second is quite short but if there is a change, we must change everywhere the "routing" exist. Further more, it can pollute the session scope because the 'routing' string is so common.
If you really need a session helper (say: if you really need a class abstracting a PHP session), then use the $_SESSION superglobal only inside that class (and not outside). So you have the superglobal encapsulated and you can replace it with test-doubles.
Next to that, this depends on the use of the session store. I bet it's highly dynamic, so I don't see much value in specifying array keys as constants first w/o any futher use (e.g. valid/invalid key checks aren't done).
I hope this does not sound harsh, because it's not meant so. Please ask if something is unclear or you have further questions. As jprofitt wrote in his answer, preventing magic numbers is something very useful, but I'm not totally convinced, that you actually introduce them here or if it isn't just dynamic properties (especially if you create a session store class).
Magic strings and numbers are evil -- even if you're the only one who would need to use them. All it takes is forgetting to update them in one place and your entire application could malfunction.
As you mentioned with the maintainability of using constants, they can make implementing updates a lot simpler. Another benefit is you can document them and a lot of IDEs will pick that up and give help in case you forget what MODULE_A or MODULE_B is referring to (for example). While it might make you type in some extra characters, it's better than misspelling 'routing' somewhere and having to dig through your code to figure out why you're getting an error.
When I develop my PHP code I always write it a certain way, I use the following to define my variables:
$sString
$aArray
$bBoolean
$oObject
$mMixed
$iInteger
$a_sString/aArray/bBoolean ect (for function argument)
Hence s, a, b, o, m, i ect. I know there is a name to call this type of writing, but I have totally forgotten it.
My question: What is this called?
It's similar to "Hungarian", but it's actually PAHN.
It's called hungarian notation.
Note: There are many different "flavors" of Hungarian. "Hungarian" by itself describes the practice of prefixing variable names with a few characters that provide additional information about the contents of the variable. What kind of information is what defines the actual flavor in use.
Hungarian notation is an identifier naming convention in computer programming, in which the name of a variable or function indicates its type or intended use.
this is some kind of hungarian notation, but some kind (seems to be very close to pahn) of the missunderstood and useless one*. take a look at joels great article about hungarian notation and how to use it the correct way.
*just using a prefix to see what type a variable should be isn't very useful - it you be way better to prefix them with something that defines what kind of variable this is. an example:
lets assume you have some variables containing different currencys (euros and dollars, in cents for your case as you havnt given a prefix fpr floats, so i'll use integers) and a function to ververt one to another. in your case:
$iPriceAmerica = 500;
// would be the right way
$iPriceEurope = iEuroFromDollar($iPriceAmerica);
// looks right and is possible as both are integers
// but is wrong (correct executable code, but doesn't give the expected result)
$iPriceEurope = $iPriceAmerica;
with correct hungarian notation, using dol_ for dollars and eur_ for euros:
$dol_PriceAmerica = 500;
// would be the right way
$eur_PriceEurope = eur_from_dol($dol_PriceAmerica);
// looks wrong - eur isn't dol, there muste be some kind of conversion
$eur_PriceEurope = $dol_PriceAmerica;
I have some code I'm working with that was written by the guy before me and I'm trying to look it over and get a feel for the system and how it all works. I am also fairly new to PHP, so I have a few questions for those willing and able to provide.
The basic breakdown of the code in question is this:
$__CMS_CONN__ = new PDO(DB_DSN, DB_USER, DB_PASS);
Record::connection($__CMS_CONN__);
First question, I know the double underscore makes it magic, but I haven't been able to find anywhere exactly what properties that extends to it, beyond that it behaves like a constant, kind of. So what does that mean?
class Record
{
public static $__CONN__ = false;
final public static function connection($connection)
{
self::$__CONN__ = $connection;
}
}
Second, these two pieces go together. They are each in separate files. From what I've read, static variables can be referenced in the same way as static functions, so couldn't you just call the variable and set it directly instead of using the function?
I get the feeling it's more involved than I am aware, but I need to start somewhere.
This isn't a magic variable. The person who wrote that shouldn't really use double underscores for variable names like that because it can cause confusion.
This is just a static property on a class. Which means it is shared between instances of that class (in the same php request).
Have a look at the docs for static properties if you're unsure on how these work.
There are several predefined "magic constants" that use this naming style. However, I don't think the underscores mean anything special (as far as the language is concerned); i.e. defining your own variable like this won't bestow it any magical properties. It may be part of the previous programmer's naming convention, and if so, it's probably ill-advised.
Setting a property via a function can, in many circumstances, make the "client" code more resilient to changes in the implementation of the class. All implementation details can be hidden inside the method (known as a "setter"). However, there are strong feelings about whether this is a good idea or not (I, for one, am not a big fan).
Two underscores do not make a variable magic.
It's better to use getters/setters than to access class properties directly.
The PHP manual has this to say on naming variables (and other symbols) with underscores:
PHP reserves all symbols starting with __ as magical. It is recommended that you do not create symbols starting with __ in PHP unless you want to use documented magical functionality.
Pay particular attention to the use of the words "reserves" and "documented". They mean double underscores shouldn't be used for user-defined symbols as it may lead to future conflicts, and that unless the symbol is explicitly mentioned in the manual as being magic, it's mundane.
I haven't done much programing in many languages, but I know in C(++), you have to declare a variable type (int,char,etc).
In PHP you, of course, don't have to do that. You can start with $str = "something"; then later $str = array("something" => "smells"); and she is happy.
How does PHP compile? How does it know what the variable type is going to be? Does it even care?
This question has no relevance to anything I am doing. I am just curious.
EDIT.
I need to clarify this question a little bit.
In C, if I say:
int y;
It reserves x amount of bytes for y. If y overflows, bad news.
PHP doesn't have this nature (at least I don't think it does).
$i = "a number";
$i = 8;
$i = 1000000000000000000;
It's all the same to the language. How does it know how much to reserve? Or am I comparing Apples to Oranges? If I am going about this wrong, do you have any good topics that I can read to better understand?
Since you have C experience, consider a PHP variable to be like the following:
typedef struct {
int varType;
void* data;
} variable_t;
when you create a new variable, it will create a variable_t, give it a type tag (lets say 1 for int, 2 for string), and store it in a list somewhere (by scope, reference by name). The actual contents will be stored in *data. When the variable is again accessed, the type can be determined from int varType, and the appropiate action taken on void* data, such as using it as an int or string.
Imagine that the PHP snippet:
$data = 12;
$data2 = $data + 1;
$data = "Hello";
produces instructions like the following (pseudo-Cish):
Line 1:
variable_t* var = new_variable(TYPE_INT, data);
store_variable("data", var);
Line 2:
variable_t* var = get_variable("data2");
if (var->varType == TYPE_INT)
int value = 1 + *(int*)var->data);
else
error("Can't add non int");
var = new_variable(TYPE_INT, value);
store_variable("data2", var);
Line 3:
variable_t* var = get_variable("data");
if (var)
destroy_variable(var);
// Do like line 1, but for TYPE_STRING
This type of augmented data works in bytecoded, compiled, or direct interpreted languages.
There are more details in regards to different virtual machine implementations (local allocation, heap allocation, register VMs, etc). If you actually want to understand how virtual machines work, the best reference is Lua. Very clean language, very clean bytecode, and very clean virtual machine. PHP is probably the last implementation you should look at.
PHP doesn't really compile -- it is interpretted (into op-codes).
Pretty much if you try to do something on a certain data type that can't be done, it'll give you an error. There is no type checking.
It doesn't compile. It is an interpreted language, like Javascript.
I realize this is an old question but here is some more specific information on how PHP handles the questions asked by the OP.
This is the page from the PHP reference that you'd want to start with:
Introduction to Variables
I know linking isn't preferred but that link should be stable and I don't want to wholesale copy PHP reference documentation. Here are the highlights.
OP: How does PHP know what type of variables it uses (or does it)?
PHP is written in C and uses a C struct typedef which it calls a zval along with a C union typedef which is calls a zval_value to represent all variables.
typedef struct _zval_struct {
zvalue_value value; /* variable value */
zend_uint refcount__gc; /* reference counter */
zend_uchar type; /* value type */
zend_uchar is_ref__gc; /* reference flag */
} zval;
"The engine attempts to cover up the complexity of the concept of a variable that can be any type by providing a uniform and intuitive set of macros for accessing the structures various fields."
"PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting." Reference Counting and Copy-on-write (COW) are two powerful concepts PHP uses which I won't go into here but are worth reading about.
"Weak typing is implicit of the engine's preference to convert, or coerce variables into the required type at execution time. Reference counting is the means by which the engine can deduce when a variable no longer has any references in the users code, and so is able to free the structures associated with the variable."
"The zval_value is a union which can represent all types a variable may hold."
" ... a variable can be of one type, the variable data is represented by the appropriate field in the zval_value union. The zval itself holds the type, reference count and a flag to indicate if a variable is a reference."
How does PHP compile?
"Compile" is a broad word that can have different meanings and PHP doesn't compile in the traditional sense. It does do a sort of pre-compilation which converts the source code into opcodes which are instructions that can be executed by the processor. These opcodes are cached which prevents PHP from have to parse frequently called scripts.
How does it know what the variable type is going to be? Does it even care?
As already quoted above it is the PHP engine's "preference to convert, or coerce variables into the required type at execution time." Baiscally PHP does always store what it determines a variable's type to be when it's created but when a variable is referenced PHP makes another determination of what the type is based on the context in which it is being used.
"PHP is weakly typed, as such the engine provides API functions for converting variables from one type to another."
The engine has a set of macros it uses for working with the zvals to convert a variable's type so that you usually don't have to deal with that.
If you want to see what zvals look like in action they can be dumped with:
debug_zval_dump($variableName);
"How does PHP compile? How does it know what the variable type is going to be? Does it even care?
This question has no relevance to anything I am doing. I am just curious."
PHP is an interpreted language and it doesn't compile.
PHP doesn't know what type the variable is going to be, because the type of the variable is determined by the type of the value which was assigned last time to that variable.
You can do this:
$foo = 5;
var_dump($foo);
$foo = "5";
var_dump($foo);
$foo = array();
$foo[] = 0;
$foo[] = 1;
$foo[] = 2;
var_dump($foo);
As you can see, whenever a value is assigned to foo, the type might be changed.
PHP doesn't care about the type of your variable, because it's a programming language and it doesn't have feelings.
EDIT:
"Or am I comparing Apples to Oranges?"
Yes, you are. Here you can learn more about the language.
EDIT2:
PHP is a scripting language with an interpreter and without a compiler. This means that in PHP you can only get runtime errors. It's a liberal language if we consider types, because you have the right to do anything with your variables and their types will be modified according to the usage of them.
These links might be useful for you:
http://www.gidforums.com/t-11866.html
http://www.webmaster-talk.com/coding-forum/186350-fundamental-differences-between-php-c-c.html
Variable scope difference between PHP and C: block scope is not exactly the same?
Note, that PHP is executed on the server, if you want to create client events, Javascript is my suggestion.
PHP now supports type hinting, you can include things like 'string' or 'array' in function definitions that are caught as the scripts are parsed to indicate there is a type mismatch.
i use certain suffix in my variable name, a combination of an underscore and a property. such as :
$variable_html = variable that will be parse in html code.
$variable_str = string variable
$variable_int = integer variable
$variable_flo = float variable.
Do you have other visual clues? Maybe something you write for variable, function name, class strucure, or other stuff that helps others to read and not only for compiler ?
You're describing Hungarian Notation: Do people use the Hungarian Naming Conventions in the real world?
There's lots of discussion on Stack Overflow about people's feelings on the topic.
It nearly is Hungarian Notation, but when you use Hungarian Notation it is more common to use a prefix instead of a suffix.