279: PHP Bad Magic

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Shawn
David Q
Ken F
Tony L
Frank W
Jeff K
Shelby C
S Ferguson
Boštjan O
Matt L
Dmitri G
Knut E B
Marcus
MikePageDev
Rodrigo C
Billy
Darryl H
Mike W
Holly S
Peter A
Ben R
Luciano N
Elgimbo
Wayne
Kevin Y
Alex B
Clayton S
Kenrick B
R. C. S.
ahinkle
Enno R
Sevi
Maciej P
Jeroen F
Ronny M N
Charlton
F'n Steve
Robert
Thorsten
Emily
Joe F
Andrew W
ulrick
John C
James H
Eric M
Laravel Magazine
Ed G
Jackson W


PHP Internals News: Episode 100: Sealed Classes

PHP Internals News: Episode 100: Sealed Classes

In this episode of "PHP Internals News" I talk with Saif Eddin Gmati (Website, Twitter, GitHub) about the "Sealed Classes" RFC that he has proposed.

The RSS feed for this podcast is https://derickrethans.nl/feed-phpinternalsnews.xml, you can download this episode's MP3 file, and it's available on Spotify and iTunes. There is a dedicated website: https://phpinternals.news

Transcript

Derick Rethans 0:14

Hi, I'm Derick. Welcome to PHP internals news, the podcast dedicated to explaining the latest developments in the PHP language. This is episode 100. Today I'm talking with Saif Eddin Gmati about the sealed classes RFC that they're proposing. Saif, would you please introduce yourself?

Saif Eddin Gmati 0:31

Hello, my name is Saif Eddin Gmati. I work as a Senior programmer at Italy. I'm an open source enthusiast and contributor.

Derick Rethans 0:39

Let's dive straight into this RFC. What is the problem that you're trying to solve with it?

Saif Eddin Gmati 0:43

Sealed classes just like enums and tagged unions allow developers to define their data models in a way where invalid state becomes less likely. It also eliminates the need to handle unknown subtypes for a specific model, as using sealed classes to define models gives us an idea on what child types would be available at run time. Sealing also provides us with a way for restricting inheritance or the use of a specific trait. For example, if we look at logger trait from the PSR log package that could be sealed to logger interface. This way, we ensure that every use of this trait is coming from a logger not from any other class.

Derick Rethans 1:24

I'm just reading through this RFC tomorrow, again, and something I didn't pick up on reading to it last time. It states that PHP already has sort of two sealed classes.

Unknown Speaker 1:35

Yes, the throwable class in PHP can only be implemented by extending either error or exception. The same applies for DateTime interface, which can only be implemented by extending DateTime class or DateTime Immutable class.

Derick Rethans 1:52

Because PHP itself doesn't allow you to implement either throwable or DateTimeInterface. I haven't quite realized that that these are also sealed classes really. What is sort of the motivation behind wanting to introduce sealed classes?

Unknown Speaker 2:06

The main motivation for this feature comes from Hack the programming language. Hack contains a lot of interesting type concepts that I think personally, PHP could benefit from and sealed classes is one of those concepts.

Derick Rethans 2:18

What kind of syntax are you proposing?

Saif Eddin Gmati 2:21

The syntax I'm proposing actually there is three syntax options for the RFC currently, but the main syntax is inspired by both Hack and Java. It's more similar to the syntax used in Java as Hack uses attributes. Personally, I have been I guess, using attributes from the start as I personally see sealing and finalizing similar as both effects how inheritance work for a specific class. Having sealed implemented as an attribute while final uses a keyword brings more inconsistency into the language which is why I have decided not to include attributes as a syntax option.

Derick Rethans 2:56

In my opinion, attributes shouldn't be used for any kind of syntax things. What they should be used for is attaching information to already existing things. And by using attributes again, to extend syntax, you sort of putting this syntax parsing in two different places , right? You're putting it both in the syntax as well as in attributes. I asked what the syntax is, but I don't think he actually mentioned what the syntax is.

Saif Eddin Gmati 3:20

The syntax the main set next proposed for the RFC is using sealed and permit as keywords we first have the sealed modifier which is added in front of the class similar to how final or abstract modifiers are used. We also have the permit clause which is basically a list allows you to name a specific classes that are able to inherit from this specific type.

Derick Rethans 3:43

So when you say type here, is that just interfaces and classes or something else as well?

Saif Eddin Gmati 3:48

It's classes interfaces and traits. Traits are allowed to add sealing but they are not allowed to permit. Okay for example, an interface is not allowed to permit a trait because a trait cannot implement an interface

Derick Rethans 4:03

In the language itself, when does this get enforced?

Saif Eddin Gmati 4:06

This inheritance restriction gets enforced when loading a class. So let's say we are loading Class A currently if this class extends B, we check if B is sealed. And if it is we check if B allows A to extend it. But when loading a specific sealed class, nothing gets actually checked. We just take the permit clause classes and store them and move on.

Derick Rethans 4:32

It only gets checks if you're trying to implement an interface.

Saif Eddin Gmati 4:36 This gets enforced when trying to implement an interface, extend that class, or use it trait.

Derick Rethans 4:41 Okay. What are general use cases for this feature?

Saif Eddin Gmati 4:45 General use cases for a feature are for example, implementing programming concepts such as Option which is a type that can only have two subtypes. One is Some, other is None. Another concept is the Result where only two subtypes are possible, either success or failure. Another use case is to restrict inheritance. As I mentioned before, for example, logger trait from the PSR log package is a trait that implements some of the method methods in logger interface, and expects whoever is using that trait to implement the rest. However, there is no restriction by the language regarding this, we can seal this trait to a logger interface ensuring that only loggers are allowed use this trait.

Derick Rethans 5:34 When you say that Option has like the value Some or None, just sound like an enum to me. How should I think differently about enums and sealed classes here?

Saif Eddin Gmati 5:43

Enums cannot hold a dynamic value. You can have a value but you cannot have a dynamic value, however, tagged unions will allow you to implement option the same way. Tagged unions are that useful only for this specific case, there is some other cases such as the one I mentioned for traits that cannot actually be implemented using the tagged unions. There is also the I don't know how to say this. Let's say we have a type A that sealed and permitting only B and C. And this case A on itself, as long as it's not an abstract class, is by itself a type. Can be used as a normal class, you can create an instance and use it normally. However with tagged unions, the option itself would not be a type, you either have some or none. That's the main difference between tagged unions until classes

Derick Rethans 6:37

A tagged union PHP doesn't have them. So how does a tagged union relate to enums?

Saif Eddin Gmati 6:43

With tagged unions as the, there is an RFC that's still in draft, I suppose that uses actually it is built on top of enums that that's why.

Derick Rethans 6:55

I reckon once that gets closer to completion, I'll end up talking to the author of that RFC. So something I'm wondering, can a sealed type permit only one other type? Or does it have to be more than one?

Saif Eddin Gmati 7:10

No, it can permit only one type. Let's say we have class A that only permits B. However, another thing is class B does not actually have to extend A, like if A is permitting B, B does not actually have to implement A. It's still useful because another class called C can extend B and implement A, so an instance of A B can still exists.

Derick Rethans 7:36

I'm not quite sure whether I understood that. If you have an interface that says A permits B, then B is not required to implement A, mostly because the moment you loads class B, you don't even know it exists, right? Because it doesn't refer to it.

Saif Eddin Gmati 7:54

Yes.

Derick Rethans 7:55

It's just going to break anything?

Saif Eddin Gmati 7:57

Hopefully not. The only break would be in the new reserved keywords which are sealed and permits. So those cannot be used as identifiers any more, but depending on the syntax choice, if for example, the second syntax choice wins which that would only take the permits keyword. If the third syntax choice is chosen then no new reserved keywords will be introduced so there will be no breaks.

Derick Rethans 8:29

From what I see in the RFC the first syntax is using both sealed in front of a as a marker and then using permits. With the second syntax, you don't use seal but you infer that it is sealed from the permits keyword I suppose. And then in the last option you use the for keyword instead of permits and also don't use sealed yet?

Saif Eddin Gmati 8:51

The third syntax choice is will be the one with no breaks as we will not be introducing any new keywords; for is already a reserved keyword in PHP.

Derick Rethans 9:02

What is your preference?

Saif Eddin Gmati 9:03

Personally I prefer the first syntax choice as it's the most explicit. When you start reading the code you can tell from the start this is a sealed class without having to continue reading until you reach permits.

Derick Rethans 9:15

I think I agree with you there. Beyond the syntax is there anything else that needs to be changed in PHP itself?

Saif Eddin Gmati 9:22

The only other change that will be introduced in PHP is in reflection class. A new method called isSealed will be added to reflection method, which allow you to check if a class the class being reflected is sealed. Another method will be added called getPermittedClasses which returns the list of class names provided in the permits clause. Also a new constant should be added to reflection class that is is_sealed constant which exposes the bit flag used for sealed classes. Some changes will happen to the getModifiers method in reflection class. This method will return the bit flag is sealed set, if the class being reflected is sealed. The getModifierNames method will also return the string sealed if the bit is set, that should be about it.

Derick Rethans 10:12

Basically everything that you need in reflection to find out whether it's a sealed class and other permits.

Saif Eddin Gmati 10:18

Yes.

Derick Rethans 10:20

See, I see the name of getPermittedClasses has to use, has the word classes in it. Does that mean that the types after permits have to be classes?

Saif Eddin Gmati 10:32

No, they can be either classes or interfaces. But PHP refers to both classes and interfaces as classes in the reflection. So we have a reflection class, but that's actually a reflection trait class interface. And basically everything is class-ish.

Derick Rethans 10:47

Class-ish. I like that. Did you look at some other alternatives to implementing the same feature or just the three syntax choices that you came up with?

Saif Eddin Gmati 10:56

I did not consider any other alternatives precisely as the alternatives might be type aliases, tagged enums, package visibility. But I think each of these RFCs focused on a specific problem and expanding that area, while sealed classes focuses on all the problems mentioned on in this RFC tries to solve them in a minimal way. But only in relation to inheritance in classes, interfaces, and traits.

Derick Rethans 11:24

Keeping it short and sweet. What has the feedback been so far?

Saif Eddin Gmati 11:29

The feedback has been pretty mixed. Some people are against adding more restriction to types and inheritance. But in my opinion, this is not about adding restriction, but rather providing the user with the ability to add restrictions. And we already have final classes, which a lot of people seem to dislike.

Derick Rethans 11:48

I don't understand why. But fair enough.

Saif Eddin Gmati 11:51

I have created a community poll a couple of weeks ago to gather feedback on Twitter. The results were 60% for with over 150 participants. Another poll was created by Peter on Facebook ended with 54 of people voting yes. However, such polls that do vary depending on the audience. So it can be really an accurate representation of the PHP community.

Derick Rethans 12:15

Polls on Twitter are never scientific, or they? I see that the RFC is in voting already. So for people listening to this, and if you have voting rights, then you have until when exactly?

Saif Eddin Gmati 12:28

Until the end of the month.

Derick Rethans 12:30

March 31. It says yes. Okay. Well, thank you very much for taking the time today Saif about sealed classes.

Saif Eddin Gmati 12:37

Thank you for having me. Hopefully, I get to be here another time in the future.

Derick Rethans 12:42

I hope so too. Thank you for listening to this installment of PHP internals news, a podcast dedicated to demystifying the development of the PHP language. I maintain a Patreon account for supporters of this podcast as well as the Xdebug debugging tool. You can sign up for Patreon at https://drck.me/patreon. If you have comments or suggestions, feel free to email them to derick@phpinternals.news. Thank you for listening, and I'll see you next time.




278: Time flies when talking DST

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Shawn
David Q
Ken F
Tony L
Frank W
Jeff K
Shelby C
S Ferguson
Boštjan O
Matt L
Dmitri G
Knut E B
Marcus
MikePageDev
Rodrigo C
Billy
Darryl H
Mike W
Holly S
Peter A
Ben R
Luciano N
Elgimbo
Wayne
Kevin Y
Alex B
Clayton S
Kenrick B
R. C. S.
ahinkle
Enno R
Sevi
Maciej P
Jeroen F
Ronny M N
Charlton
F'n Steve
Robert
Thorsten
Emily
Joe F
Andrew W
ulrick
John C
James H
Eric M
Laravel Magazine
Ed G
Jackson W


Vim throwdown, Part 2

In this episode, Jake and Michael continue their journey down the Vim rabbit hole, and cover thrilling topics such as the leader key, custom bindings, vim-test, and Git worktrees.

This episode is sponsored by Workvivo and Makeable.dk and was streamed live.

Show links


277: PHP, Baseball, Cars, and Grifters

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Shawn
David Q
Ken F
Tony L
Frank W
Jeff K
Shelby C
S Ferguson
Boštjan O
Matt L
Dmitri G
Knut E B
Marcus
MikePageDev
Rodrigo C
Billy
Darryl H
Mike W
Holly S
Peter A
Ben R
Luciano N
Elgimbo
Wayne
Kevin Y
Alex B
Clayton S
Kenrick B
R. C. S.
ahinkle
Enno R
Sevi
Maciej P
Jeroen F
Ronny M N
Charlton
F'n Steve
Robert
Thorsten
Emily
Joe F
Andrew W
ulrick
John C
James H
Eric M
Laravel Magazine
Ed G
Jackson W


PHP Internals News: Episode 99: Allow Null and False as Standalone Types

PHP Internals News: Episode 99: Allow Null and False as Standalone Types

In this episode of "PHP Internals News" I talk with George Peter Banyard (Website, Twitter, GitHub, GitLab) about the "Allow Null and False as Standalone Types" RFC that he has proposed.

The RSS feed for this podcast is https://derickrethans.nl/feed-phpinternalsnews.xml, you can download this episode's MP3 file, and it's available on Spotify and iTunes. There is a dedicated website: https://phpinternals.news

Transcript

Derick Rethans 0:15

Hi, I'm Derick. Welcome to PHP internals news, a podcast dedicated to explain the latest developments in the PHP language. This is episode 99. Today I'm talking with George Peter Banyard, about the Allow null and false at standalone types RFC that he's proposing. Hello, George Peter, would you please introduce yourself?

George Peter Banyard 0:36

Hello, my name is George Peter Banyard. I work on the PHP language, and I'm an Imperial student in maths in my free time.

Derick Rethans 0:44

Are you're trying to say you're a student in your free time or contribute to PHP in your free time?

George Peter Banyard 0:49

I feel like at this time, it's like, both are true at the same time.

Derick Rethans 0:53

Let's hop into this RFC. It is titled allow null and false as standalone types. What is the problem that it is trying to solve?

George Peter Banyard 1:02

This is the second iteration of this RFC. So the first one was to just allow null initially, and null is the unit type In type theory parlance of PHP, ie the type which only has one value. So null is a type and a value. And the main issue is that when for leads more with like inhabitants, and like the Liskov substitution principle. If you have like a method, like the parent method, which can be told like either null or an object, and your implementation in a child class always returns null, for various reasons, maybe because it doesn't support this feature, or whatever is out, or... If your child method only returns null, currently, you can't document, that you can't type this properly, you can document it in a doc comment or something like that. But due to how PHP type handling works, you need to specify at least like another type with null in the union. Basically resort to always saying like mimicking the parent signature, when you could be more specific. This was the main use case I initially went into.

Derick Rethans 2:08

If I understand correctly, you can't just have an inherited method that has hinted as to just return null?

George Peter Banyard 2:14

Exactly. If you always return null, maybe because you always work or something like that, then you must still declare the return type as like null or exception, which is not a concrete because you say what, like why never fail. And like static analysers, if they can figure it out that you're using a child class, they can't maybe like do some assumptions or work further down that like what you're doing is redundant or things like that. So that's one of the main reasons I initially went with it. And I didn't add false initially, because it was like, well, false, it's not really a type properly. It's, it's what's called a value type. False is one value from the Boolean type. And I was like, Well, okay, we're just going to limit it to like, being the type theory purist, limited to proper types, where null is a proper type, although it's a bit sometimes misunderstood, I feel in the PHP community at large. And then people were like, well, if we add null, then by the only type-ish thing, which you can use in a type declaration, or whatever, which can't be used in a return type on its own, is false. And it's just weird. So why not add it in full. So that was the second thing as to why I added it. Some of PHP internal's functions being terribly designed because they were designed back in the early noughties, return null on success and false on failure, which you can't probably type at the moment. Currently, we need to type them as like Boolean or null, but true can never be returned in this case. And there are some other some other people have reached out to me it's like, well, yeah, but I always return false in this case. Or I also return always true in this case, although true, we have this weird asymmetry that we have false as a value type and not true.

Derick Rethans 3:49

What was the reason for having false but not true?

George Peter Banyard 3:53

When the union type RFC got discussed and passed for PHP 8.0, false was added, because a lot of traditional behaviour of PHP internal functions, was to return false on failure, instead of the technically more correct thing would be to return null. Because loads of functions return a false on failure, and saying that like in returns, these types, or a Boolean would be basically lying because you could never have true, false was included in it. With the restrictions that you can only use false as the complement with other types. So you need to do for example, array, or false, you couldn't just use false.

Derick Rethans 4:37

Would it also mean that you can define a return type of a method that inherited a method that returns a bool, as false?

George Peter Banyard 4:48

Yes, that would be now possible with the amended proposal. Yeah, which goes back to this weird a symmetry, we're probably. Adding true to make a complete would be a future RFC to do.

Derick Rethans 5:00

Now, we've talked about return types. But I guess the reverse applies to arguments?

George Peter Banyard 5:06

Arguments and property types also would, would be allowed to, like declare themselves as like null or false. The usefulness here is way more limited. Because if you declare an argument to be of type null, then basically you can only ever pass a null to it. And then therefore, the type doesn't do anything.

Derick Rethans 5:26

But in an inherited method, you could then widen it.

George Peter Banyard 5:31

Yes, exactly. You could always say: Well, this argument exists, it's always null. If you extend like your class or message, then you can add other types. But in theory, you can already do that by adding like an argument at the end of the message, because that's LSP compliant. The case for, and properties of those, because they are typing, they're in like their beads. Kind of debatable why you would do that. But it's just that like, well, if you accept types at one point, just restricting them like somewhere else gets very weird. At this point is more like look at the human review, or like use static analysis for the analyser to tell you like this argument is redundant and just remove it or this property doesn't make any sense. Because if it can only ever be null, why does it even exist in the first place?

Derick Rethans 6:13

Right now, you can already use false in union types, but why not with null or false?

George Peter Banyard 6:19

That goes back to the when a union type RFC got introduced. Null got added as a keyword. Before you could only use the question mark, before a type to make the type nullable. If you have a more complex union type, to not use the question mark in front of it. Therefore, the null keyword got added as a proper type. And because the logic was, Well, you shouldn't ever be able to return just null. Because then that function is kind of equivalent to void. Because of that, it was said that like, Well, okay, null and false basically have like kind of the same status is that like, if you just want to use null on its own, you're doing something kind of weird. And if you're returning more than false, like that signature is very strange. I think when that was discussed, nobody knew initially that an actual PHP function within one of the extensions, like in core had such a weird signature. Which mainly, we just started discovering that after this got, like accepted and we could like actually start properly typing the internal functions, and then you discover these weird edge cases where sounds like, that's a bit strange, can't properly document it. We just need to make like a note on the PHP documentation side. And like the type signature kind of lies to you. PHP's type hierarchy is a bit strange, void kind of lives on its own. So if the function is marked as void, it must always like any child inheritance, or whatever needs to be void. And when you type return in the function body, you need to always use return with like a semicolon afterwards, you can't even return null. Although, under the hood, PHP will always return a value when you call a function, even if the function is void, which will be null.

Derick Rethans 7:58

The RFC also talks about question mark null, what is that supposed to be? Is that null or null?

George Peter Banyard 8:03

PHP has this limited type redundancy checks at compile time. It will basically check if you're duplicating types. So if you write for example, int or int, even if it's capitalized differently, PHP was like, okay, just specifying twice the same type in this union. This is redundant. And then it will throw a compile error, we're basically saying, maybe you're just doing a mistake, maybe you meant something else. In the same vein, basically the question mark, gets like, translated into like, any seeing pipe null. And so if you write null with a question mark in front of it, it's just saying like, well, you're doing null or null, which is basically redundant. Therefore, you'll get like a compile time error telling you like.

Derick Rethans 8:41

That seems sensible to me. What's been the feedback so far?

George Peter Banyard 8:45

The most feedback, I think I've got it when I first proposed it in October. And at the time, it was like, Yeah, this is useful. And it's kind of needed because well, having always more type expressiveness is I think, good in general. But the main feedback at the time was like, Well, why not include false? The other feedback I got was basically, well, for consistency, what shouldn't you also add true? Yes, I do agree with this. I frankly, find it very strange that we landed in this situation where we only have one of these value types, either true and false, or none of them would make more sense to me. But that's expanding the scope. And it's kind of not this is not really concerned with this specific detail. Probably next, another RFC to do, for either myself or somebody else. It's just like propose true as a value type.

Derick Rethans 9:33

Is the implementation of this RFC complicated?

George Peter Banyard 9:36

It's very simple. It basically removes checks, because currently in the compile step, which checks for like type signatures, it needs to check that like, Well, are you declaring false or are you declaring null, and these checks get removed, so it makes the code a bit more streamlined. Oh, there's one change in reflection. For backwards compatibility reason, because of the fact of the question mark, any union type which is composed of a only two types, where one of them is null,will get converted in reflection to use the question mark notation, which is kind of a bit weird because it then gets converted into like a name type instead of a union type in reflection. But that's there, because of backwards compatibility reasons. I am breaking this into the more sensible reflection type. If you have a type of null and false, then you'll get a reflection union type instead of a named. From my understanding from reading the reflection code, the intention was always probably in PHP 9, to remove this distinction. So if you get a named type, it's only a single type instead of a possible nullable type. And any nullable types get converted into like a reflection union type when you have like null and the other type. Maybe this is a good test case to see if your code breaks.

Derick Rethans 10:50

I would probably call that a BC break though.

George Peter Banyard 10:53

This only happens if you do false union null. You can't use false currently on its own. And I think like, if you get false, as a named argument type, with like a question mark in front of it. Because it would be completely new, and you would never deal with it. It's like, well, this can also break because false can be in the Union type. If your library or the tool supports union types with the reflection thing, it will automatically know how to deal with false because it needs to know how to deal with it. And null. So that was kind of also the logic. It's like, well, okay, like if the tool supports that, which it needs to, then if you put this case into that bracket, it will work. It makes kind of the reflection code a bit more complicated at the moment. The whole fact that we need to juggle between like figuring out like, should we use the old like the backwards compatible thing reflection of like using a name type instead of the Union type, if there's a null and depending on the type, makes a reflection code unwindy and everything. And we have like a special function in C, which is basically just like, okay, which object do I need to create, depending on this type signature?

Derick Rethans 11:53

When do you think you'll be putting this up for a vote?

George Peter Banyard 11:56

I suppose I could put it up for vote immediately now. I am planning on maybe putting this on to vote at the end of the week or something like that.

Derick Rethans 12:04

Well, thank you for taking the time today to talk about this RFC.

George Peter Banyard 12:09

Thank you for having me on the podcast.

Derick Rethans 12:13

Thank you for listening to this installment of PHP internals news, a podcast dedicated to demystifying development of the PHP language. I maintain a Patreon account for supporters of this podcast, as well as the Xdebug debugging tool. You can sign up for Patreon at https://drck.me/patreon. If you have comments or suggestions, feel free to email them to derick@phpinternals.news. Thank you for listening, and I'll see you next time.