 This presentation is called the Anatomy of PHP, a PHP request by Herman Radke and Joel Salas. Herman is a sociologist, turned full-stack engineer, turned VP of Engineering. He writes PHP during the day and is a Slashian at night. He spends a past of his life writing a lot of C and used that knowledge to maintain the PECL Slashman cache and the PECL German extensions. He is passionate about creating and consuming hyper-media APIs in particular how. And then Joel is a seasoned engineered and emerging engineering leader and employed at Nordstroms with the online off-price division. He has been using computers since before he can read. In the former live, he cut his teeth in the traditional CIS admin role. Nowadays, he is glad to usher in the age of DevOps and writes software to solve systems and platform systems and platform problems. Hi, everybody. Let's see, those come back up here. Awesome. So let's get started. We're gonna talk about the Anatomy of a PHP request. So those are really good introductions, but just to recap real quick, who are we? Joe and I both work for Hope Look, who was acquired by Nordstrom a couple years ago. We also built Nordstromrack.com. So that's sort of the context where we're coming from. Most of our software is written in PHP at this point. Again, I'm the VP of engineering at Hope Look. I've been there for six years. That's my information if you want to follow me. This fine dress gentleman to my left. I'm Joe Salas. You might remember me from yesterday. I did the Hoot performance, cracking the flash sale talk. And so a lot of the nitty-gritty stuff we didn't get to yesterday. We're actually gonna revisit here today. You can tweet at me on Twitter. You can post request me at Good Hub and you can email me at Gmail. Thank you. So let's get into it. We're gonna talk about anatomy here. So a quick definition. For context, this talk actually was sort of the precursor to the Hope performance talk. So if you saw that one, we're doing it in reverse. But we're gonna dig into the internals of what a PHP request and that request lifecycle looks like. And we're gonna get into some of the internals on how that's structured because they're closely related. And we can think about it a little bit sort of like human anatomy, right? We're not gonna sort of extend that metaphor too much but there are different sort of parts that make up PHP and the request while working in concert. It's just similar to the human anatomy work. So let's get into the process lifecycle first, right? So you can see here, we have a diagram and the orange section represents a PHP process. Also known as a PHP worker if we're actually running it. And inside there's a blue section who sections the request lifecycle. And we're gonna go into detail about this but just from a high level, there's sort of formate steps, right? We have a module initialization step. We have a request initialization step and a request shutdown and then a module shutdown. That happens around your actual user landscape. And then we also have, let me drill. We also have the PHP internals. And what this looks like roughly is we have sort of four main boxes here, right? So we have the SAPI top which are sort of our survey API. We have extensions, we have core and then the ZenTube engine. So when we're working with PHP, a lot of the time we will actually be, not just running PHP raw code from the command line, we're actually going to be using PHP in the context of a web request. And so some basic understanding of HTTP here is pretty important so that we understand that a PHP request doesn't really leave an isolation. It's going to be bound pretty much on both sides by something that speaks HTTP. For example, the PHP prior to seven do not have a built-in HTTP server so you can't just run PHP as a process and have it listen to HTTP requests and speak that protocol. And so what I'm gonna focus on here for a few minutes is the server API or SAPI part. And so when we're dealing with that, we're talking about a couple of two main ways to run a PHP service on the web. We're gonna be using either ModPHP which is a C extension for Apache. And so that is some C code that is written, that hooks, sorry, the web server hooks into when you are running a web request. And the other way, the more modern way, the way that Holtlook does it, is going to be the NGINX proxy to PHP FBM through the FastCGI protocol around CGI protocol. So again, we're visiting this. The interactions in blue are bounded by the web server. So whether we are initializing class executing PHP, all of that is between the web server's interaction. So let's begin to learn more about what ModPHP does. Touch on this a little bit. But who here has actually set up a web service, WordPress or something like that, using ModPHP with Apache? So that's almost everybody in the room. So this is an extraordinarily common way to run a PHP service. What are the issues? I say yes. Let's look at what that might look like in a sort of system diagram. So the black box represents the Apache web server. And these three fellows should look pretty familiar right now, they are of course PHP workers. And so the way this works is for every single Apache worker thread that is serving HTTP requests, you're actually bound into the ModPHP C module and that will run PHP code. And so the nice aspect of this is that a lot of people don't actually know the difference between what Apache is doing and what PHP is doing because it's so tightly integrated. And that can be problematic because it can be difficult to help people debug. Are you having a problem with PHP? Is it a web server problem? And again, this is I think where a lot of people are not familiar with the boundary lines because ModPHP really blows in a lot. And so it is again using the server API directly. So what are some problems with this design? The problem, one direct problem is that you need one worker to serve every web request and you are loading PHP for every web request. So is every web request PHP related? I imagine if you put everything correctly that answers no, you're gonna be serving all kinds of static assets, flat HTML, your NEP and geographic stuff like that to sort up the markup of the page. And that's not all going to necessarily have to be fed from a PHP worker. So this actually does not scale all that well. It uses lots of memory, uses lots of CPU and it actually falls over pretty easily. So now we do something a little different. This is the sort of fast CGI or FPM model. And if you're running with Nginx, you notice that Nginx is hard. And it's hard to get Nginx going with WordPress. If you're used to ModPHP, it just works, right? You just like get install PHP and installs HTTP for you and you have everything you need out of the box to run WordPress. And so once you get to Nginx, it's a very different beast because it's kind of hostile. And so the Nginx supports really forces you to understand the difference between the fast CGI server and the Nginx web server. So here we can see a couple of worker threads and they're running, speaking of fast CGI protocol. And Nginx, we should actually see some HTTP traffic coming in here from your right. And the traffic comes in, comes right back out. The fast CGI response goes to Nginx. Again, you can't talk to the fast CGI server directly with the web browser. You will get garbage because it does not know what HTTP is. So the black box there is actually the boundary of the fast CGI process manager. So who here remembers the old and horrible days of FCGID or something terrible like that? Great, yes, those are dark days, right? And part of the problem was that the fast CGI ID, all you could do is tell it to run a certain number of workers and it would spin them up. And so if your Nginx web server is all of a sudden under a lot of load, you're shooting tons of CGI requests of these things and eventually you're gonna get some dreaded, dreaded gateway timeouts because what is the gateway timeout? Like gateway, okay, okay. Like a router? My router's gonna have no. It means that Nginx is trying to speak upstream to the fast CGI server. There's nothing there to service the request. And so it's actually gonna return an error to the user or cause a cascading or somewhere down the line. And so nowadays we use the fast CGI process manager which will run a pool of workers. It will track them. It will keep them alive and healthy and happy and it will actually kick them if they die. And you can actually set a dynamic scheduler there. So you can set a minimum of five workers, let's say a maximum of 40 and you can have them scale up and down dynamically based on the number of fast CGI requests coming in from the web server. So this is arguably a better way to do things. So, cool. So we have some context about how we're like providing stimulus to PHP, right? It's through the context of a web server. And even if we're in the, a CLI sort of context, when you make a command line request to PHP, so like even PHP dash H, you really are sort of following that same step. You're actually spinning up a little miniature web request, executing it and then spinning it back down. So actually actually they peer in a little bit more into the internals of PHP here. And let's first talk about the Zen 2 engine, which is sort of the underpinning of PHP. So this Zen 2 engine sort of has three main roles. It's a compiler, so it takes your PHP script and it turns that into ICODE. It's a virtual machine, a virtual machine that is able to take that by code and execute it. And it also is a memory manager, so it's able to allocate resources. And you can think of that sort of like the brain of a PHP process or request. And then next we have core. And core really has two main focuses, it's network IO and file IO, and then request management. Request management in the sense that this is where your main sort of other program comes in and it's sort of gonna pass control off to the SAPI or to the extensions. So extensions really is where all of the functionality of PHP comes in. And as we saw a few minutes ago, extensions and modules are the same thing, so we have to initialize all those things. So when you actually look at a PHP source code, you'll notice a folder called ext, and that's where all the extensions are. And there's like 30 or 40 in there. When you build PHP, all those are built into PHP, automatically, statically compiled. And that's where all the functionality, like the standard library, quote unquote, of PHP, that's where all that lives. Everything from like math to JSON to MySQL, it's don't confuse it with the standard PHP library extension, it's quite confusing, but all this functionality statically compiled into the binary, and when the process starts, all these are initialized. And there's also another kind of extension, which was the PECO extension, or PHP community library, PHP extension community library. And this is where extensions like Memcache and Mongo and Redis exist and Gearmin exist. And these are normally dynamically compiled into shared libraries, DLLs or .so files. This is really nice from an operational standpoint because you can sort of include these or remove them from your servers without having to rebuild the entire PHP executable. So up until this point, like we really haven't been able to actually make a request to PHP yet, we've been sort of setting everything up. And so, we're up there. What we normally sort of start at is our index PHP file, right? Like a lot of us run an NBC framework. So in order to execute that get request, the first thing we're gonna do is initialize our modules. So every single one of those extensions that I just talked about, whether it's statically compiled or dynamically compiled, it's gonna actually have a callback that PHP is going to call when the process starts. And PHP, as we all know, uses INI files for configuration. So PHP's gonna go through and look at all those INI files, whether it's PHP INI or using the PHP.D with all your extension specific INI files. It's gonna figure out what extensions that you have either statically compiled in or that you're dynamically loading into the process. It goes through every single one of those extensions and it gives it that callback. And we call that M and N, or module and N. And again, a module and extension is sort of an interchangeable thing here. So during this extension startup, the process has started and we're gonna sort of define things here that are common across requests. So whether the common things are cross requests, these are things like constants. So you might think like the JSON extension that's statically compiled in has like a bunch of different constants like the JSON hex tag constant. That actually is only like sort of defined constantly during this process startup. It doesn't sort of exist sort of just in PHP itself. We do other things such as I have to actually specify what classes exist in PHP that are part of your extension. So the way PHP 5 works, your extension only knows like C functions, right? You have to actually map like, hey, this object state, like this class method with object state maps to this sort of C function and you do that during modules initialization. You also register resources. So I'm sure if anyone has sort of like a VAR dump on a MySQL database connection, the type is resource. You have to register those resources at this point. And you're gonna register stream wrappers, but I can go too much on those. And you can also register globals. And so you might wanna do this if you need to actually have a global variable with some value that's common across all your requests. So you have an example of this while like this might be useful. So who here is using Redis for session storage, any kind of database type stuff, key values, great. So who is using Redis with PHP? Interesting. So hopefully you are using Redis in an HA fashion, meaning that if one of your Redis servers dies, your PHP workers don't utterly bomb. But one of the things that we can do in this eminent phase and one of the best examples of how that works is actually Redis Sentinel. Is anyone actually using Redis Sentinel in production right now? Interesting. So to be clear, we had some trouble running Redis Sentinel for a number of different reasons. But when we took a crack at it, it was not very mature, we were using Memcache. And so our eventual solution was Couchbase. But let's go through the Redis example here and let's talk about it. So what is Sentinel? Sentinel is an HA option for Redis and what that will do is maintain all of the typical things you wanna know about a cluster, like which of the members, which ones are active, which ones are inactive, it will handle failover and it tries to allow the clients to be good customers of the data store. So first thing we're gonna do as an example here is let's create a global variable to track the Redis server. So in our Sentinel code or however we want to manage the Redis extension, we're going to keep track of that Redis server. How we find that Redis server is not super important yet but you can sort of assume that Sentinel will actually provide us this value. So if that fails, we're gonna query Sentinel again and find the Redis server that we can actually talk to. And then finally, we're going to persist that value across multiple requests. So in other words, that becomes a global ineminent and as we establish, eminent means that it applies to every single request after the PHP work has been initialized. And it's important to note that there is a difference between the worker and the request and there are some frameworks or one times it would have you where this distinction is not very well maintained. But in PHP, you can be pretty sure that if there was a 500 or something like that, the worker itself is not gonna come crashing down. Your request might return a 500 or it might bomb for some other reason or a memory but the worker itself is still basically intact. And part of the reason is that the eminent process does maintain some state between requests. And so there are actually some problems with this design. Remember, we're trying to query Sentinel to know which servers are available, which ones are not. And at the same time, we're saying we're gonna persist this in some sort of in memory store, right? So this becomes a problem if our configured Redis server is actually unavailable after eminent. So remember that little blue chunk of the diagram are actual requests coming in and coming through. And so eminent happens before those requests are actually being executed. So in other words, this worker after the server is down will be stuck until you go in and you hop it or the process manager is hopped or what have you to clear this value out, query Sentinel and talk to a new Redis server. And so this is actually the problem that prevented us from deploying Redis into production. So it's part of the design problem with Redis. It's also a design, part of a design issue with PHP itself. And so there's good reasons and bad reasons to do that. But this is one limitation that we've certainly encountered. Yeah, so after eminent, the next phase is our net or in other words, requesting it. So in requesting it, this is actually where we actually get the property of Joe, which I touched on is where every single request that comes in, it does not affect any other requests that are currently being served out of PHP, FPM or the existing Apache plus mock PHP setup. So if one of our requests sort of blows up, I think it's affected. So we could have a scope operating environment, right? And then just like eminent, every extension has the ability to set a callback for requesting it. And so you can loop through every single one of the requests extensions again and sort of ask that extension if they want to do something in this phase. This is actually kind of really interesting if you know PHP has an auto prepend file sort of configuration where you can provide some user land or just normal PHP code that gets prepended to every single request. It's kind of a nice feature. Some people use it. You can think of our net as sort of the built-in C level version of auto prepend file. So you can do some interesting stuff here. Some things like profilers or debuggers will use this to set up an environment. To kind of put that in perspective, like if you want it to, you could write in your R and it, you can basically start a microtimer and initially capture that value at that point and to be used later to sort of track how long a process took if you wanted to do that. So to this point, like all that we've done is sort of set up PHP to actually start talking to the PHP script that we want to actually sort of get executed, right? So that's where our actual domain logic exists. So if you type, again, you type PHPs hmini command line, you'd have done all this work to get to this point. So now we want to actually talk about sort of some of the execution phase sort of structure that PHP has. So most commonly people are using a MVC environment, maybe a page controller architecture if you're using a really old application. If you're in MVC, the most common thing you're gonna do is require some files and then you're gonna configure your application, right? Configuring your application is normally done with a nini file, xml file, or a JSON file and you have to do this every single time, parse that into some values that PHP understand and then pass that into your application. So none of this, there's no sort of coordinates in PHP to preload this stuff, to skip this sort of bootstrap phase. So this is sort of, we're starting to sort of do things, common things that we're gonna do every single request over and over in userline code. The JVM or Node.js, or if you're in Ruby with Puma or Unicorn, you can preload like your framework level or application level code so that when requests come in, you don't have to do this step and PHP, you can't do that. And so as soon as we start our application, our framework probably uses some sort of auto-order. Most of us, if we're familiar with PHP argument, using Composer, right? Modern frameworks contains hundreds of classes. So at this point, once your application starts starting up and it wants to get sort of the foundation of the framework loaded, it's going to be going through and try to auto-load all these different files. So auto-loading is really just include or require underneath the hood. And if we're sort of following PHP conventions, we normally have one class per PHP file. So the auto-loader is now loading up all these different files. And that means we're basically doing a stat or an F-stat against the file system to basically see if that file exists where we think it exists and then load it up. So it's a lot of work against the file system. This only exacerbated if you use a PHP directive called include paths. So I'm sure most of us have more than one include path. So you could actually, if you have 10 include paths and your framework codes at the end of that include path, you could be doing a thousand different requests to the file system just to sort of get your code into a state that the framework's loaded up. So you can actually start doing your specific domain logic. And not all sort of libraries follow the PHP sort of auto-loading conventions of PSR zero or PSR four. And if that's the case, we can sort of use this alternative thing, which is a class map, right? And class maps are really just normal PHP code that has an array where the key of the arrays, the class name and the value is the location. So this actually can get around some of the penalties of auto-loading. We have an absolute path to the file so we don't take an include path hit through lowers the amount of times we're statting the file system. And since it's just normal PHP, once we load it up once for that request, like it's there at key value, a key lookup in an array of PHPs is the hash table lookup. That's constant time, it's very fast. You don't want to overdo this the right because if there's 10,000 entries in this array and you're only using like 10% of them, that's a lot of overhead. You're loading up a PHP file and you're only using 10% of it. So class map can be used for things, PHP unit, for example, doesn't auto-load very nicely since Sebastian didn't sort of build the structure of his code in a way that made sense to PSR zero or PSR four. So class map works really well here. Normally if you're using a normal namespace code, you can sort of just get away with using the regular auto-loader. So at this point, we're probably thinking like PHP is a pretty bad way to serve web requests. All this seems really slow, but we can do a couple of things. Yeah, it's looking at dark days for PHP, right? Who, after sort of watching all of the stuff that we're gonna do on every single web request, would still write their app in PHP? We've got a couple of die-hards, but yes, it is doing lots and lots of extra work and like I said yesterday, PHP is very CPU hungry, but what about upcache? Hopefully you are all using upcache, as everyone familiar with APC, the extension, and everyone loading that up, great. So APC is the absolute fastest easiest way to take your very slow application and make it significant and less slow, but probably still a little slow. So what are we doing with the upcache? What does that actually mean? What can upcache possibly do in this routine to actually make the situation better? So what the upcache will do is actually load up the, as you run a web request, the very first web request through the system will actually create some in-memory operation upcode caches that every subsequent web request can reuse and therefore it's sort of exploiting nice warm cache of code paths to use so that you do not have to trash all of the nice objects, maybe that you've loaded into memory or have you. It actually does not pop in on an object level, but it's gonna be a little bit more level and it's going to optimize the most commonly used parts of your code. So let's take an example. Let's write a simple PHP script here. It's going to echo hello scan and let's look at what this might end up looking like if we were to dump out the upcache in the middle of a server request here. So this is actually pretty straightforward. Sign hello, press scale, echo, return null. So this is a pretty simple representation, but this is a very clear way to see that as we tick our PHP code, which is meant to be dynamic, we're actually turning it into something static in the upcode, I'm sorry, in the upcode cache. And that is a trade-off, of course. We're losing some of the dynamic features of PHP where you could possibly edit something on the file system and see results immediately. But the benefit is that things can be anywhere from 10 to 1,000 times faster depending on the code path that your application takes. So again, what they remember, upcache is global. So this affects every single web request. So if, for whatever reason, the upcache needed to change between requests, you're going to either have to go into the upcache manually because it's a very painful process, I do not recommend trying it, or you're going to have to hop the worker and let it re-go through the entire eminent R&D process and execute the code again before the upcache is cleared and ready to go. So I can think of about 10 different ways that this can bite us. Deployments are one. So if your deployment scheme consists of copying a tarball and untarling it, that's not going to cut it. You need to kill the PHP process and that might incur some sort of minor downtime, right? So now you need to plan around the HA just to make this upcache thing work. So this requires a little bit of planning but I absolutely think it's worth it. So another thing that you're going to want to get the upcache flatten your config files. This is huge. So as you mentioned that config files are typically going to use are going to be an INI file, maybe a YAML, probably a JSON. And your PHP code is going to have to go through this, parse it out and try to make some sense of it for every single web request. You cannot upcache a JSON file. You cannot upcache a YAML file but you cannot upcache a PHP file. And so what a lot of web frameworks will do is something called a cache warmup phase. Do you have any symphony users in the house? Great, so we've got like one or two hands that I can see and so as you are well aware, either on a first request or using the cache warm symphony console command, you're actually going to create a temporary directory particularly to the symphony environment that will contain a large number of PHP files and this is basically parsing through your parameters that YAML or whatever the rest of the config is and writing out some PHP code that the opcode can cache. And so this is extremely important for performance reasons but again it's also something where if you change the config file, that's not a dynamic file anymore. You need to go in and clear the cache and out the process to clear the, to edit the config file, recompile the PHP on disk cache and then recompute the opcode cache and now you're actually serving all the requests again. So as we mentioned before, you can create a class map to keep track of all of the different classes that you're going to be loading to prevent false to the disk. You can cache the class map and this is awesome because if you have a nice well-designed class map that includes everything you need and not a lot that you don't, you can cache the opcodes around the class map and reach your classes a lot faster. You can cache file paths. So as you'll recall, you're going to be doing a lot of stat operations to make sure that the files are what we expect them to be because we're auto loading because we're doing convention over configuration all these other nice developer friendly practices. They're actually pretty rough on your system. So if we're caching the file paths, that means that we do not need to stat the file system every single time we're looking for a file. We can safely assume that the file didn't move and in a lot of cases, you'll find that if you go into your running PHP server and just start deleting shit, it's going to keep working. It will actually work for you surprisingly long time. In fact, you don't really need a file system after a while if you're running the opcache correctly and I'm not recommending this by any means, but the opcache is surprisingly resilient at actually doing real code, code paths when it's nice and warm and when it's well configured. So you can keep connecting to my SQL, you can keep using extensions. Your code at some point actually becomes somewhat optional. And one nice config option you can set for the opcache extension, which it is an extension just like everything else, set stat to zero, meaning don't run to the file system. After you load up all of the files, just never check it again. There are certain cases where it might have to check the modification time or maybe in strange cases, the hash of the file. Just don't do it, just never do it. And this kind of gets you into that scenario that I was describing where you don't ever touch the file system after the opcache is nice and warm. So great, so up until this point, we've not made it a lot of the issues with sort of bootstrapping PHP requests at this point. And we can get some decent performance. And so back to this notion of a request being independent. What we do on our request is independent of every other request. So our global opcode cache is gonna help us with some of the bootstrap time, but again, we don't have preloading for other things. So one of the most common things to do in our bootstrap is to actually load a connection to our database, right? We're using MySQL in this example. And one of the things that happens when you actually use PDO, which is PHP data objects and probably the most popular way to make a database adapter to PHP is the constructor actually makes the connection to your database, whether it's MySQL or Postgres or MySQL. So in your bootstrap code, which is sort of at the very beginning of your request before you done any domain logic, you're not making a network request to the database. You actually might not even need a request to the database at this point. You might be using Memcache to serve out your response or maybe you're just sort of calculating some value to return back or you're just serving up a piece of markup for the webpage. And you made a connection to your database. So the opcode cache cannot help with this sort of instance, right? This is outside that sort of balance of the opcode cache. You can do something like wrap PDO and sort of try to lazy load this, but it's important to know that this can be really expensive and sort of, but most people don't realize that this sort of is happening behind the scenes until we start having really bad performance problems because they don't understand that the sort of context with PHP runs in. Which is everything sort of independent. Nothing's preloaded. And we go through this sort of request life cycle every single time. So at this point, we've bootstrapped our code. We have sort of done some domain or business specific thing that's important to that request. And it's now actually time to get another response section. So PHP was written to template HTML. And it's optimized for that. So by default, when you start trying to return strings back to the webpage, you're streaming those strings back, which is great because if you're into performance, you're time to first buy it looks pretty good. But remember, we're in a HTTP context here, right? And so HTTP has a header section in the body and when you start sending back your HTML or your JSON or your XML, that goes in the body. But in HTTP, in your HTTP request, oftentimes you need to actually send back some headers. Especially if you are using a session or you wanna actually specify that your content type that JSON is, application JSON. So what normally happens when people use the streaming part of PHP is they get a sort of warning like this. And that's because we've intermittently actually streamed some content back. And we're already in the body section of the response. And once you're in the body section, you can't sort of say, hey, stop request, I wanna sort of jump back into those headers and add some headers. So, most of you may not know this, but PHP session handling is driven by cookies and cookie, a cookie is HTTP header. So if you run into this, you can get this from warnings, you can get this from notices too. So it's not just sort of intentional echoes or print statements that you have in your code. If you have a notice or a warning because you index and arraying correctly, you can inadvertently sort of block the ability to send back your cookie session header or run a similar important header to your application. So there's a way around this that PHP gives us, which is to buffer our output. So OB stands for output buffering. So what happens here is inside our PHP process, we can actually dedicate some memory to start storing the values into that buffer before we send it back to the client right away. This does slide on the type of first byte because we're not shooting it, but it also gives us the ability to wait until the very end when we're actually already to start responding. We've all set our headers correctly. Now we can start sending our body back. This is really useful too because we bring in a lot of different libraries with things like Composer and stuff. And you don't want sort of one misbehaving library to accidentally write something out and sort of wreck the rest of your request and sort of be trying to figure out what happened there in real time. So in this example here, we can start the buffering and you actually can do a lot more configuration with output buffering and flush is sort of ending the output buffer, ending the buffering and then flushing it out to the client right away. You can have a lot more sort of configuration. You also can kind of stack this too, which is pretty nice. And so at this point, once we're done with our output, our request is done and we're entering the request shutdown phase of the lifecycle. You can think about our shutdown here as sort of the opposite of R&D in that it's auto append file. The nice thing about our shutdown as opposed to auto append file and user line code is it's guaranteed to run. So if you ever have tried to use auto pre-con file or auto append file, you will notice that if someone just sticks an X or a die in their code and you're using auto append file, your auto append file doesn't actually get added, which shouldn't be quite annoying if you are really depending on that code to run. So if you really actually need that to run, you can stick that in the R shutdown phase and see. But this also is important because PHP at this point is freeing the scope environment. It's freeing up all that memory. This can be leaky at times. If you have cyclical references, sometimes PHP can't free them, which is not good. And this is why I'm first conscious about how Apache and the fast process manager work. They only sort of let so many requests run through that process before they'll tear it down. Like you'll bring up your process, allow a request to be sure that you'll tear it down. It's because we don't actually capture all of that memory that we allocated back, back to the Apache or FPM process or the file system. So we sort of need to free that up. Not much else interesting goes on in R shutdown. Most extensions don't make too much explosive use of it, but it is there again. Every extension is sort of called during that phase. And finally, when our process is setting down, we also have a hook for M shutdown. M shutdown is really just again, freeing all that memory that the eminent had. So if you actually did register globals or you did register resources, you wanna free those all up as well before you just lay that memory out on the floor. So real quick, to go back, PHP really has sort of four things it has to do before it can execute any sort of user line code. And there's sort of two related life cycles. The first life cycle is our module initialization or extension initialization and extension shutdown. And the second cycle in there is a request initialization or request shutdown. On end and R shutdown are gonna happen many times per process and typically eminent and M shutdown happen once per process. And also PHP is made up of four basic building blocks here, right? The Zen 2 engine, which is what the engine that runs PHP5 is. The core, which we really don't actually get much into and then the extensions and the SAPI. So we're at the end of our talk here. Does anyone have any questions? Yes, sir? Yeah, so HHVM is interesting. It probably is quote more analogous to the replacing of the Zen 2 engine. It still has SAPI's, it still has extensions and it still has a core. And if anyone here is not familiar, that's a Facebook sort of answer to a better way to serve PHP requests. Under the hood it'll compile things down to C++. There's a couple of obstacles to try to move over from PHP to HHVM, which is a primarily being that extension part. But the lifecycle actually is much the same. There are some differences, you know, HHVM allows you to add in like things like async. So now you're introducing this concept like a threading model, a concurrency model inside of PHP requests. You don't typically have when you're running like the Zen 2 engine, right? Like you can run threaded workers in PHP, I think he will do that, but most people don't really run threads in their PHP userline code. You can kind of start doing that in HHVM. So it does violate some of this what we talked about and you do have to write the extensions custom to HHVM there. But it's fast, it's really fast. And I think it's sort of PHP's built to make getting a web application up and running pretty easily from an operational standpoint, right? Like that's the whole idea behind it. HHVM sort of trades that ease of operation for like speed, what you got to have it baked in. So I mean it depends on what your tradeoffs are. We all look, we have moved over to HHVM yet because there is sort of like a beauty to be able to quickly sort of deploy new software. And we've sort of able to kind of kick that can down that road as much as possible. But if we needed to optimize more, certainly that would be something that you should look at. And specifically if you're implying why are we not using HHVM, the short answer is doctrine. So who knows what doctrine is or who uses doctrine? So for whatever reason, the doctrine test pass on HHVM but when we try to use it, it bombs. And so instead of digging into why doctrine is bombing on HHVM, we're like, you know, we're actually gonna write some software that makes us money. That's why we have not touched HHVM for now. Any other questions? Yes, sir. So the question was in the PHP init process, when are the extensions loaded for a worker? And the answer to that is they're not loaded for any particular worker. They're gonna be loaded when we initialize module in a minute and that applies to every worker. So yeah, so you're gonna first do, your worker is a process, right? So Apache or FPM? FPM's easy because it's just PHP. So FPM is gonna create a new process. And when that process gets created, you first go into core, which is your main. And what main does is it basically says, hey, I'm gonna start the init process. And it goes through and it says, hey, I'm gonna look at my INI file. I'm gonna go find out every extension that's been registered. So there's a lot of extensions that are baked in by default. And they also go about that entry in your INI file where you have extension equals like memcached.sl. So it reads all those. And then there is some magic that as internal like some extensions, I think like EXT standard has to be loaded prior to all the other extensions because it's just PHP magic. But after that, it's kinda just okay, go down like this list of extensions and call that eminent on it. Once that minute's done, like that extension is registered in PHP. PHP knows that it's there because that's part of that eminent process. And so you actually can, a lot of times what you'll do is you'll register a constant that says like this is the version of that extension. You'll register a constant about the constant about the name of that extension. And you can kind of even ask like PHP-m right or PHP-i that I'll list it off that point. Yeah, so it's not done before the worker is created. So a worker in a process are gonna be equivalent. I think that's the key. So like if you think about it at HOTELOOK, we do the flash that like ADM, right? We're doing tens of thousands requests a second. And if we have a request worker that basically, that's a hundred requests go through and then it gets reaped by the process manager and it starts up again. Like we're actually reading processes at a pretty quick space at 10,000 requests a second. And there really is a, after a while you start to feel the tax of 30 extensions loaded up in PHP, even if they're statically compiled in, you start to go through 30 or 40 eminent callbacks. And those eminent callbacks are doing work, right? They're registering, they're allocating memory. They're registering constants. That's the out memory allocation and the class registrations memory allocation. So it's doing a fair amount of work to sort of bootstrap the process itself before it can even start serving requests. Correct, yeah, I think I forgot to mention that. After eminence is done, you're basically in a spot where you can now serve a request. You can, you're listening for a request at that point. And I'll leave. Yeah. One thing I wanted to add to that is if you actually go into your at cphp.d or whatever the directories and your distribution, you'll notice that all of the extension config files are named like encrypts and so on and so forth. And some of them on distributions will be prepended with a number. And so what that does is it's just alphabetical and that's how it loads the extensions. And so the extensions that must be loaded first will be, you know, zero, zero, or 10 or 20 or what have you. And so the distribution containers actually know which extension need to be loaded first. But if it wasn't for that, it would basically be tribal knowledge where you'd have to know, oh, you have to load that one first or you're gonna crash every time. Yes, sir. So the question was, when I say that we're gonna flatten an INI file, like what does that mean? Right, so an INI file is chock full of, you know, keys and values usually. And again, the point is that the opcode cache cannot optimize for that INI file. And so what we're gonna do is we're going to write out some PHP code that embeds those values into the code. So it basically is inlining all of the values that you're going to need. And this is actually gonna be particular to each framework. So this is some pretty low-level framework code that you're talking about, whether it's Zen or Symphony. Those developers have maintained a way to take a config file and pre-compute a PHP level cache. Yeah, we ran into that for the early days. I hope like, oh, what you basically can do then is, you can have a step in your sort of build process, which is you can, again, you can write some PHP code that says, and just that INI file, and just basically spit out the INI file in like a PHP array and then save that PHP file to disk. And then you can make sure that your code loads that file up, that PHP array, instead of parsing the INI file, that PHP array will be saved in the opcode cache. So you might flatten it. You're flattening from like this, you know, an INI format into this native PHP format so the opcode cache can save that PHP code. And then you can sort of sidestep the whole parsing in the INI. And then it's there like in opcode cache memory for the next request. Yeah, you could. If you wanted to add eminent, you could write a custom extension at eminent to load those INI values into memory at that point. And you could serve them out that way because you can expose those values to a huge amount PHP. Yeah. You're getting into like Yahoo, what Yahoo did for many years and what Facebook did for many years before they wrote hip-hop. That's basically what their strategy was. Yes. Yeah, so we're a number of us are in the process. So the question was, are we on the process of evaluating PHP 7? Yes. Yeah, so for extensions, they changed all the extension format, right? So there's actually a webpage that lists what all the state of the extensions are. I know Gearman was taken up by Co-People out of Etsy and they upgraded that extension code to be PHP 7 compatible. A lot of extensions that are sort of on the fringe have not been updated and that's a sort of, if you depend on one of those, it's gonna be, you can't upgrade. But the popular ones like Memcache D and Gearman and those are being upgraded and you could cut over. And it does change, like the internal, what we talked about here is basically the same, but they did a bunch of optimizations to really the ZVM to sort of make PHP faster and by doing so they kind of had to change the API with how extensions sort of saved values and interacting with user land come. And that's what a lot of the blocker is. So if we go back to our anatomy diagram, really we're talking about just that lower third of the picture, that's been the majority of what they changed and so all of the interactions that have to be reconsidered and redesigned. But most of the stuff that we talked about today is going to be valid in PHP 7. I'm very excited for it. I think it's a significant upgrade and it really brings PHP into 2016. I'm not aware of anyone that's using PHP 7 in production. I think a lot of the sort of PHP hackers went with HHVM and that might dramatically change with the right software. But we're definitely going to be using PHP 7 as soon as we can. Sort of took the best, it took the best stuff that hip hop did, HHVM didn't and put it in the PHP 7. So I think it depends on your workload, it doesn't have anything like benchmarks can lie, right? So I think depending on your workload, you can get a better perf boost over HHVM and some other cases HHVM can give you better perf boost over PHP 7. So I think if you are just looking for any sort of speed up over six, I'm sorry, five, because six doesn't exist. Seven's fine, like you should just do seven, right? Because it's easy. If that's not applicable to your workload then you may think about HHVM. But to PHP 7 is much easier to operationalize than HHVM. Just to be clear for all our purposes, like one of our applications, the actual time we spend in PHP is only 50 milliseconds and all the rest is external web calls and database activity. And so if we, let's say it was significantly faster, which in our case would be like 20% faster, we just went from 50 milliseconds to 40 milliseconds and it doesn't affect the number, it doesn't really do much. And that's really what you're going to see when you tune the heck out of your PHP, so. There are certain workloads that are not e-commerce related that will benefit significantly, like ETL, anything involving lots of data manipulation, not like SQL queries, but actually loading stuff into doctrine and dealing with objects. That's very CPU intensive and the initial stuff we've seen for doctrine on PHP 7 is very, very promising. Yes, sir. The question was, what tools are you using for profiling and debugging? I could probably think of at least 10, but the main ones that we use on a daily basis are going to be using New Relic for application performance monitoring. And so it has a very, very, it basically knows about Zen and it knows about symphony, so with almost no instrumentation, you can start getting stats on which APM points are getting the most use. Right, so the plugin is actually an extension and the extension writes to a small local demon that acts as a proxy up to New Relic. And so you have to load in that extension at a certain time, like we said. We've also used Blackfire to drill down into specific problems. So we use New Relic to find out where there could be a problem and then once we know where the code path is that has a problem, we use Blackfire to actually profile that application call and figure out why it's so slow. We've used XHFROF and XDBug in the past, but I think for the most part, New Relic has kind of superseded those. And honestly, if you're actually really trying to figure out what is going on with the PHP call, if your code is swallowing errors, like just crack out good old S-trace and just attach to a process ID and watch it go by and see what it's actually trying to do, we've uncovered a number of file system related, network related issues that way. And so in some ways, the old ways are the best ways in terms of debugging some of the stuff. New Relic actually is a Zend extension, so we didn't cover that in the talk, but it's super low level. Anybody else have a question? All right, I think that's it. Thank you very much.