PHP Internals News: Episode 76: Deprecate null, and Array Unpacking – Derick Rethans

PHP Internals News: Episode 76: Deprecate null, and Array Unpacking

In this episode of “PHP Internals News” I chat with Nikita Popov (Twitter, GitHub, Website) about two RFCs: Deprecate passing null to non-nullable arguments of internal functions, and Array Unpacking with String Keys.

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, a podcast dedicated to explain the latest developments in the PHP language. This is Episode 76. In this episode, I’m talking with Nikita Popov about a few more RFCs that he has been working on over the past few months. Nikita, would you please introduce yourself.

Nikita Popov 0:34

Hi, I’m Nikita. I work on PHP core development on behalf of JetBrains.

Derick Rethans 0:39

In the last few PHP releases PHP is handling of types with regards to internal functions and user land functions, has been getting closer and closer, especially with types now. But there’s still one case where type mismatches behave differently between internal and user land functions. What is this outstanding difference?

Nikita Popov 0:59

Since PHP 8.0 on the remaining difference is the handling of now. So PHP 7.0 introduced scalar types for user functions. But scalar types already existed for internal functions at that time. Unfortunately, or maybe like pragmatically, we ended up with slightly different behaviour in both cases. The difference is that user functions, don’t accept null, unless you explicitly allow it using nullable type or using a null default value. So this is the case for all user types, regardless of where or how they occur as parameter types, return values, property types, and independent if it’s an array type or integer type. For internal functions, there is this one exception where if you have a scalar type like Boolean, integer, float, or a string, and you’re not using strict types, then these arguments also accept null values silently right now. So if you have a string argument and you pass null to it, then it will simply be converted into an empty string, or for integers into zero value. At least I assume that the reason why we’re here is that the internal function behaviour existed for a long time, and the use of that behaviour was chosen to be consistent with the general behaviour of other types at the time. If you have an array type, it also doesn’t accept now and just convert it to an empty array or something silly like that. So now we are left with this inconsistency.

Derick Rethans 2:31

Is it also not possible for extensions to check whether null was passed, and then do a different behaviour like picking a default value?

Nikita Popov 2:40

That’s right, but that’s a different case. The one I’m talking about is where you have a type like string, while the one you have in mind is where you effectively have a type like string or null.

Derick Rethans 2:51

Okay.

Nikita Popov 2:52

In that case, of course, accepting null is perfectly fine.

Derick Rethans 2:56

Even though it might actually end up being different defaults.

Nikita Popov 3:01

Yeah. Nowadays we would prefer to instead, actually specify a default value. Instead of using null, but using mull as a default and then a

Truncated by Planet PHP, read more at the original (another 13687 bytes)

Successful refactoring projects – Set the right goal – Matthias Noback

Refactoring is often mentioned in the context of working with legacy code. Maybe you like to define legacy code as code without tests, or code you don’t understand, or even as code you didn’t write. Very often, legacy code is code you just don’t like, whether you wrote it, or someone else did. Since the code was written the team has introduced new and better ways of doing things. Unfortunately, half of the code base still uses the old and deprecated way…

The need to be consistent is very strong in me, and from what I see, in many developers. And so we get started and improve every bit of code everywhere. We become frustrated if we don’t “get” time for this work from our managers (“they don’t get it”), or if we can’t finish it; look at all this code, it’s so outdated!

Consistency is a bad refactoring goal

I’ve had this experience many times, and it made me give up on consistency as a refactoring goal. Not because consistency isn’t good. It is, because the uniformity of a code base makes it easier to contribute to it. Fewer surprises means you’ll make fewer mistakes. The problem is consistency as a refactoring goal:

  1. It’s an all-or-nothing goal. Either the code base is consistent, or it isn’t. When you start the project, you have to finish it, or you’ll feel unsatisfied. This conflicts with what we established in the previous post: prepare to stop at any time.
  2. It doesn’t serve a goal for other stakeholders. Which means it will be very easy for managers to pull the plug on the refactoring project, if they find out it’s costing them valuable development hours.

Higher refactoring goals

When it comes to refactoring projects, the development team (or sometimes just one developer) is often the primary stakeholder. Some common refactoring goals that developers have are:

  • Being able to upgrade to PHP 7
  • Being able to run tests without an actual database
  • Being able to rely on static analysis for support during development

When discussing these goals in a meeting with business stakeholders, they will downplay them because they don’t seem relevant for their own cause. They aren’t right, of course, because what’s great about these “developer-oriented” goals is that they actually serve higher goals, goals that also serve business stakeholders:

  • Being able to keep the software running for years to come
  • Being able to develop new features faster
  • Being able to release fewer mistakes to production

Alignment

Very often when I find my refactoring projects being postponed or blocked, I realize it’s because I didn’t explain the higher goals. What is often really useful is to talk to other stakeholders or decision makers (and pardon the management speak):

  • What are the benefits they’ll notice, e.g. being able to work faster, release fewer mistakes, those are really good selling points for a refactoring project.
  • Explain that you can work on this for just a few hours each week and still get those benefits. This can take away the fear that this may be one of those endless projects that have to get cancelled in the end. It forces you to think about refactoring steps, and being able to stop (and start) at any time.
  • What are things you’ll unlock for the company, e.g. by deploying your application as a Docker image, deployments will be a single-step process, which finishes in a matter of seconds.

Conclusion

In summary, for a successful refactoring project you need to be able to stop and continue at any time. Establish refactoring goals that serve higher goals. Explain to business stakeholders that your development goals have benefits for them too.

Once the refactoring project gets the green light from the team, the next task is to determine refactoring steps. To be continued!

Successful refactoring projects – Prepare to stop at any time – Matthias Noback

Refactoring projects

A common case of refactoring-gone-wrong is when refactoring becomes a large project in a branch that can never be merged because the refactoring project is never completed. The refactoring project is considered a separate project, and soon starts to feel like “The Big Rewrite That Always Fails” from programming literature.

The work happens in a branch because people actually fear the change. They want to see it before they believe it, and review every single part of it before it can be merged. This process may take months. Meanwhile, other developers keep making changes to the main branch, so merging the refactoring branch is going to be a very tedious, if not dangerous thing to do. A task that, on its own, can cause the failure of the refactoring project itself.

Short-lived branches

So can’t we use a branch for refactoring? Of course we can. But it has to be a short-lived branch. How can you ensure that a branch is short-lived?

  1. It has small commits, created within small time intervals (e.g. minutes, not hours)
  2. Each commit passes all the tests (meaning the actual tests pass, and static analysis yields no errors)
  3. The branch can be merged and deployed at all times (and actually, should be merged regularly)

Following this set of rules is a great idea for any branch, not just refactoring branches. But it’s even more important there, since the changes are likely to span many, and remote parts of the code base, which makes the risk of merge problems bigger.

What often happens is that we change a method in a way that requires updating all its clients. It takes a lot of time to do this work, and so we end up with either a very large commit, or a commit that just takes a lot of time to make, meaning that we don’t follow the first rule of short-lived branches.

Something else that could happen is that we are just viciously updating code all around the code base, and we commit the changes because everything seems alright, but then our quality assurance tools tell us something is wrong. When we get the results back from CI, we add another commit that “Fixes tests” or “Makes PHPStan happy”. When working with short-lived branches, ensure that everything is okay before committing (or set up a pre-commit hook so you can’t forget to do this).

What if we have to stop now?

Creating small commits that pass all the tests, the result should indeed be that our branch can be merged at all times. This for me is closely aligned to a thought I always have in mind when programming: what if someone pulls the plug on this project today? I don’t want my effort to be wasted, I don’t want my branch to be deleted without merging. So when I work on something I always aim for it to be useful for the team, the company, its users, etc.

One way to make sure that you always add value to the project is to establish goals for which the following is true:

  • The bigger goal can be reached in a number of smaller steps
  • Each step is useful when considered on its own

We’ll take a closer look at refactoring goals in the next article.

Conclusion

Refactoring projects require short-lived branches, where every commit can be merged in the main branch immediately. You should be able to stop the refactoring project at any time, while still leaving the project in a better state.

Should we use a framework? – Matthias Noback

Since I’ve been writing a lot about decoupled application development it made sense that one of my readers asked the following question: “Why should we use a framework?” The quick answer is: because you need it. A summary of the reasons:

  • It would be too much work to replace all the work that the framework does for you with code written by yourself. Software development is too costly for this.
  • Framework maintainers have fixed many issues before you even encountered them. They have done everything to make the code secure, and when a new security issue pops up, they fix it so you can just pull the latest version of the framework.
  • By not using a framework you will be decoupled from Symfony, Laravel, etc. but you will be coupled to Your Own Framework, which is a bigger problem since you’re the maintainer and it’s likely that you won’t actually maintain it (in my experience, this is what often happens to projects that use their own home-grown framework).

So, yes, you/we need a framework. At the same time you may want to write framework-decoupled code whenever possible.

Here’s a summary of the reasons. If all of your code is coupled to the framework:

  • It will be hard to keep up with the framework’s changes. When their API changes, or when their conventions or best practices change, it takes just too much time to update the code base.
  • It’s hard to test any business logic without going through the front controller, that is, by making fake or real web requests to your application, analyzing the response html, or peeking into the database.
  • It’s hard to test anything at all, because nothing allows itself to be tested in isolation. You always have to set up a database schema, populate it with data, or boot a service container of some kind.

Pushing for a big and strong core of decoupled code, that isn’t tied to the database technology, or a particular web framework, will give you a lot of freedom, and prevents all of the above problems. How to write decoupled code? There’s no need to reinvent the wheel there either. You can rely on a catalog of design patterns, like:

  • Application services and command objects
  • Entities and repository interfaces
  • Domain events and domain event subscribers

None of these classes will use framework-specific things like:

  • Request, Response, Session, Token storage, or security User classes,
  • Service locators, configuration helpers, dependency resolvers,
  • Database connections, query builders, relation mappers, or whatever your framework calls them.

For me good rules of thumb to test the “decoupledness” of my business logic are:

  1. Can I migrate this application from a web to a CLI application without touching any of the core classes?
  2. Can I instantiate all the classes in the core of my application without preparing some special context or setting up external services?
  3. Can I migrate this application from an SQL database to a document database without touching any of the core classes?

1 and 2 should be unconditionally true, 3 allows some room for coupling due to the age-old problem of mapping entities to their stored format. For instance, you can have some mapping logic in your entity (i.e. instructions for your ORM on how to save the entities). But at least there shouldn’t be any service dependencies that are specific to your choice of persistence, e.g. you can’t inject an EntityManagerInterface or use a QueryBuilder anywhere in your code classes. Also, calling methods should never trigger actual calls to a database, even if it’s an Sqlite one.

If you do all of this, your framework will be like a layer wrapped around your decoupled core:

This layer contains all the technical stuff. This is where you find the acronyms: SQL, ORM, AMQP, HTTP, and so on. This is where we shouldn’t do everything on our own. We leverage the power of many frameworks and libraries that save us from dealing with all the low-level concerns, so we can focus on business logic and user experience.

A framework should help you:

  • Make a smooth jump from an incoming HTTP request to a call to one of your controllers.
  • Load, parse, and validate application configuration.
  • Instantiate any service needed to let you do your work.
  • Translate your data to queued messages that can be consumed by external workers.
  • Parse command-line arguments and pass them as ready-to-consume primitive-type values.
  • Turn your application’s data in

Truncated by Planet PHP, read more at the original (another 1854 bytes)

PHP Internals News: Episode 75: Globals, and Phasing Out Serializable – Derick Rethans

PHP Internals News: Episode 75: Globals, and Phasing Out Serializable

In this episode of “PHP Internals News” I chat with Nikita Popov (Twitter, GitHub, Website) about two RFCs: Restrict Globals Usage, and Phase Out Serializable.

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, a podcast dedicated to explain the latest developments in the PHP language. This is Episode 75. In this episode, I’m talking with Nikita Popov about a few RFCs that he has been working on over the past few months. Nikita, would you please introduce yourself?

Nikita Popov 0:34

Hi, I’m Nikita, I work at JetBrains on PHP core development and as such I get to occasionally, write PHP proposals RFCs and then talk with Derick about them.

Derick Rethans 0:47

The main idea behind you working on RFCs is that PHP gets new features not, you end up talking to me.

Nikita Popov 0:53

I mean that’s a side benefit,

Derick Rethans 0:55

In any case we have a few to go this time. The first RFC is titled phasing out Serializable, it’s a fairly small RFC. What is it about?

Nikita Popov 1:04

That finishes up a bit of work from PHP 7.4, where we introduced a new serialization mechanism, actually the third one, we have. So we have a bit too many of them, and this removes the most problematic one.

Derick Rethans 1:19

Which three Serializable methods or ways of doing things currently exist?

Nikita Popov 1:24

The first one, which doesn’t really count is just what you get if you don’t do anything, so just all the Object Properties get serialized, and also unserialized, and then we have a number of hooks, you can use to modify that. The first pair is sleep and wake up. Sleep specifies which properties you want to serialize so you can filter out some of them, and wake up allows you to run some code, after unserialization, so you can do some kind of fix up afterwards.

Derick Rethans 1:52

From what I remember, if you use unserialize, where does the wake up the constructor doesn’t get called?

Nikita Popov 1:59

During unserialization the constructor, never gets called.

Derick Rethans 2:03

So wake up a sort of the static factory methods to re rehydrate the objects.

Nikita Popov 2:08

Exactly.

Derick Rethans 2:08

So that’s number one,

Nikita Popov 2:10

Then number two is the Serializable interface, which gives you more control. Namely, you have to actually like return the serialized representation of your object. How it looks like is completely unspecified, you could return whatever you want, though, in practice, what people actually do is to recursively call serialize. And then on the other side when unserializing you usually do the same so you call unserialize on the stream you receive, and then populate your properties based on that. The problem with this mechanism is exactly this recursive serialization call, because it has to share state, with the main serialization. And the

Truncated by Planet PHP, read more at the original (another 22227 bytes)

PHP Internals News: Episode 76: Globals, and Phasing Out Serializable – Derick Rethans

PHP Internals News: Episode 76: Globals, and Phasing Out Serializable

In this episode of “PHP Internals News” I chat with Nikita Popov (Twitter, GitHub, Website) about two RFCs: Deprecate passing null to non-nullable arguments of internal functions, and Deprecate passing null to non-nullable arguments of internal functions.

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, a podcast dedicated to explain the latest developments in the PHP language. This is Episode 76. In this episode, I’m talking with Nikita Popov about a few more RFCs that he has been working on over the past few months. Nikita, would you please introduce yourself.

Nikita Popov 0:34

Hi, I’m Nikita. I work on PHP core development on behalf of JetBrains.

Derick Rethans 0:39

In the last few PHP releases PHP is handling of types with regards to internal functions and user land functions, has been getting closer and closer, especially with types now. But there’s still one case where type mismatches behave differently between internal and user land functions. What is this outstanding difference?

Nikita Popov 0:59

Since PHP 8.0 on the remaining difference is the handling of now. So PHP 7.0 introduced scalar types for user functions. But scalar types already existed for internal functions at that time. Unfortunately, or maybe like pragmatically, we ended up with slightly different behaviour in both cases. The difference is that user functions, don’t accept null, unless you explicitly allow it using nullable type or using a null default value. So this is the case for all user types, regardless of where or how they occur as parameter types, return values, property types, and independent if it’s an array type or integer type. For internal functions, there is this one exception where if you have a scalar type like Boolean, integer, float, or a string, and you’re not using strict types, then these arguments also accept null values silently right now. So if you have a string argument and you pass null to it, then it will simply be converted into an empty string, or for integers into zero value. At least I assume that the reason why we’re here is that the internal function behaviour existed for a long time, and the use of that behaviour was chosen to be consistent with the general behaviour of other types at the time. If you have an array type, it also doesn’t accept now and just convert it to an empty array or something silly like that. So now we are left with this inconsistency.

Derick Rethans 2:31

Is it also not possible for extensions to check whether null was passed, and then do a different behaviour like picking a default value?

Nikita Popov 2:40

That’s right, but that’s a different case. The one I’m talking about is where you have a type like string, while the one you have in mind is where you effectively have a type like string or null.

Derick Rethans 2:51

Okay.

Nikita Popov 2:52

In that case, of course, accepting null is perfectly fine.

Derick Rethans 2:56

Even though it might actually end up being different defaults.

Nikita Popov 3:01

Yeah. Nowadays we would prefer to instead, actually specify a default value. Instead of using nu

Truncated by Planet PHP, read more at the original (another 13729 bytes)

Xdebug Update: January 2021 – Derick Rethans

Xdebug Update: January 2021

Another monthly update where I explain what happened with Xdebug development in this past month. These will be published on the first Tuesday after the 5th of each month.

Patreon and GitHub supporters will get it earlier, around the first of each month.

You can become a patron or support me through GitHub Sponsors. I am currently 94% towards my $1,000 per month goal.

If you are leading a team or company, then it is also possible to support Xdebug through a subscription.

In January, I worked on Xdebug for about 60 hours, with funding being around 35 hours. I worked mostly on the following things:

Releases

The start of the month saw the release of Xdebug 3.0.2 which addresses further issues that are present in Xdebug 3.0. Xdebug 3.0.2 mainly addresses issues related to triggering features in Xdebug through Xdebug’s new modes, and a few issues related to code coverage were fixed too. Expect another bug fix release in February, as I am intending to make one release per month.

Now that the GitHub repository of the VS Code Plugin has been moved to the Xdebug organisation, there is going to be progress here too.

DBGp Client and DBGp Proxy

I have had some expert reviews of the Go code for DBGp Client and DBGp Proxy, by JetBrains developers working on their GoLand IDE. Once I have reviewed and integrated their comments I am intending to release the source code of these two tools under an open source license.

Documentation

I spent some time adding anchors throughout the documentation to make it easier to link to specific sections. The upgrade guide now also has a Japanese translation. Xdebug.org’s code has been updated to allow for translations of the documentation, although I do not have any plans to translate the rest of it at the moment. The main issue with having translations of the documentation that the translations also need updating when the original in English changes.

As part of Xdebug’s documentation efforts I created another video in my series on Xdebug 3. This new video explains Xdebug 3’s diagnostics features to help finding issues with Xdebug’s configuration and settings.

The next video that I am going to release will deal with how to trigger Xdebug’s features, such as single step debugging, profiling, and tracing.

Xdebug Cloud

The Xdebug Cloud now has a design, created by Simon Collison. With the design in place, I am currently putting the dots on the i’s related to signing up and billing. I expect to launch Xdebug Cloud for beta customers during this month.

If you want to be kept up to date with Xdebug Cloud, please sign up to the mailinglist, which I will use to send out an update not more than once a month.

PHP Internals News: Episode 74: Fibers – Derick Rethans

PHP Internals News: Episode 74: Fibers

In this episode of “PHP Internals News” I talk with Aaron Piotrowski (Twitter, Website, GitHub) about an RFC that he is proposing to add Fibers to PHP.

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 74. Today I’m talking with Aaron Piotrowski about a Fiber RFC, that he’s working on together with Nicolas Keller. Aaron, would you please introduce yourself.

Aaron Piotrowski 0:33

Hi everyone I’m Aaron Piotrowski, I started programming with PHP back in 1998 with PHP three, so I’ve just dated myself there but, but I’ve worked with a lot of different languages over the last few decades but PHP is always continually remaining, one of my favourite and I’m always drawn back to it. I’ve gotten a lot more involved with the PHP projects since PHP seven. The Fiber RFC is my first major contribution I have attempted though. In the past I did the RFC for the throwable exception hierarchy. And the Iterable pseudo type in PHP 7.1.

Derick Rethans 1:12

Yeah, these things are both before I started doing the podcast so hence we haven’t spoken yet, at least on here. We’ve actually met at some point in the past. I’ve had a read through the Fiber RFC this morning, but I’m still fairly baffled. Could you perhaps explain in short what Fibers are where the idea comes from. And what’s your specific interest is in adding them to PHP?

Aaron Piotrowski 1:35

A few other languages already have Fibers like Ruby, and they’re sort of similar to threads in that they contain a separate call stack, and a separate memory stack, but they differ from threads in that they exist only within a single process, and that they have to be switched to cooperatively by that process rather than actively by the OS like threads. So sometimes they’re called Green threads, and the generators that are in PHP already are actually somewhat similar to Fibers; but generators differ in that they’re stack less. And so what that means is that generator function can only be interrupted at one layer. Whereas a Fiber can be interrupted anywhere in the call stack. So like it’d be imagine if you had a generator where yield could be very deep in a function call. Rather than at the top level. Like, how generators can be used to make interruptible functions, Fibers can also be used to create similarly interruptible functions, but with again without having to know exactly when it’s going to be interrupted not at the top level but at any point in the call stack. And so the main motivation behind wanting to add this feature is to make asynchronous programming in PHP much easier and eliminate the distinction that usually exists between async code that has used promises and synchronous code that we’re all used to.

Derick Rethans 3:09

So what specifically are you proposing to ask to PHP here then?

Aaron Piotrowski 3:12

Specifically I’m looking at adding a low level Fiber API, that’s really aimed specifically at async framework authors to create their own more opinionated API’s on top of that low level API. So adds just a couple of classes: Fiber, and a FiberScheduler on within a couple of exception classes and reflection classes for inspecting Fibers. When a Fiber is suspended to the execution switch is to FiberScheduler, which is then a special Fiber, that’s able to start and resume, regular user Fibers. So a Fiber scheduler is general

Truncated by Planet PHP, read more at the original (another 18886 bytes)