Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Doeke Norg
Doeke Norg

Posted on • Originally published atdoeken.org

     

is_initialized() helper function

I'm no fan of (too much) global helper functions, but I feel likeis_initalized() is a must! It really should be PHP native, but that'snot happening.

Thisis_initialized() helper is modeled aftermethod_exists(), and others like it, in the sense that it asks for an$object and a$property separately. Reason for this is that we cannot infer the context of a parameter when it is passed likeis_initialized($object->parameter). The function would just receive the value.

if(!function_exists('is_initialized')){/**     * Returns whether a property is initialized with a value.     * @param string|object $object The class (name), that contains the property.     * @param string $property The name of the property.     * @return bool Whether the property is initialized with a value.     */functionis_initialized($object,string$property):bool{try{return(newReflectionProperty($object,$property))->isInitialized(is_object($object)?$object:null);}catch(ReflectionException$e){returnfalse;}}}
Enter fullscreen modeExit fullscreen mode

Why is this better thanisset()?

It's not "better" per se, but it makes memoization easier as a value can now also benull.isset() would returnfalse in that case. It kind of depends on your use case.

An example

In this example we use the$service parameter for memoization. The parameter can by eithernull or aService instance. In this case, only the first call togetService() would call the container, whereasisset() would call the container multiple times if the value where to benull.

classController{private?Service$service;publicfunctiongetService():?Service{if(!is_initialized($this,'service')){$this->service=Container::get(Service::class);// either the service, or `null`}return$this->service;}}
Enter fullscreen modeExit fullscreen mode

Think you can make this better?

Please let me know if you have an idea on how this function could be even more helpful.

Top comments(5)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
williamstam profile image
William Stam
  • Joined

Couldn't you just do something like return $this->service ?? Container::get(....):

This->service would be null if it's not "initialised" . ?? Is perfect for that

CollapseExpand
 
doekenorg profile image
Doeke Norg
Works mostly with Symfony, Laravel and a sprinkle of WordPress plugins. Testing enthusiast.
  • Location
    Groningen, The Netherlands
  • Joined
• Edited on• Edited

Not exactly; although for services aContainer is probably optimized enough. So this might not be the best example. I usually do

return$this->service??=Container::get(...);
Enter fullscreen modeExit fullscreen mode

to initialize the service, when I am 100% sure the container will return the service.

The "problem" this helper solves is that?? is the same asisset(), and that will returnfalse for uninitialzed aswel asnull. Butnull is a valid value to initialize a parameter.

So when the value is initalized withnull theisset() or?? check will still trigger theContainer::get(...) on subsequent request, while thisis_initialized() helper will not. Hope that makes sense.

CollapseExpand
 
williamstam profile image
William Stam
  • Joined
• Edited on• Edited

Isn't using reflection here somewhat adding extra load on the server unnecessarily? Normal execution, stop, check reflection, go on. Sure might be easier for developer but at the cost of runtime? The more your program does the more CPU cycles it consumes. At some point just throwing hardware at it won't be enough. And doing something like this kinda feels like that trap to me.

Not the most liked opinion unfortunately :(

Cool solution tho.

Thread Thread
 
doekenorg profile image
Doeke Norg
Works mostly with Symfony, Laravel and a sprinkle of WordPress plugins. Testing enthusiast.
  • Location
    Groningen, The Netherlands
  • Joined
• Edited on• Edited

You're right, performance is something we absolutely have to keep in mind. So I'm glad you point this out.

The Reflection API is actually pretty fast. Of course it adds some extra load, but only on execution time, and even then the differences are minuscule. I ran aphpbench test over 4 situations:

  1. multiple calls on the same instance withisset()
  2. multiple calls on new instances withisset() (every call is a new instance)
  3. multiple calls on the same instance withis_initialized()
  4. multiple calls on new instances withis_initialized() (every call is a new instance)

I ran the test with 10.000 calls, and repeated them 100 times. These are the results:

subjectrevsitsmem_peakmoderstdev
benchIsset10000100656.328kb0.087μs±6.93%
benchIssetNew10000100656.328kb0.172μs±5.01%
benchReflection10000100656.328kb0.334μs±4.14%
benchReflectionNew10000100656.344kb0.433μs±3.72%

The memory consumption is exactly the same for every situation, except situation 4. But that is not really helpfull with memoization. Even then, the extra memory is negligible.

isset() is definitely faster, but only marginal. You would probably not notice this in your request.

Still, I would only suggest you use this method if you have a memoization situation where the value of your (typed) parameter could also benull. Otherwise, definitely useisset() or??(=).

Thread Thread
 
williamstam profile image
William Stam
  • Joined

This benchmark thing is super super interesting! Thank you!!

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Works mostly with Symfony, Laravel and a sprinkle of WordPress plugins. Testing enthusiast.
  • Location
    Groningen, The Netherlands
  • Joined

More fromDoeke Norg

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp