How to persist objects between requests in PHP
Asked Answered
T

4

21

I've been using rails, merb, django and asp.net mvc applications in the past. What they have common (that is relevant to the question) is that they have code that sets up the framework. This usually means creating objects and state that is persisted until the web server is recycled (like setting up routing, or checking which controllers are available, etc).

As far as I know PHP is more like a CGI script that gets compiled to some bytecode each time it's run, and after the request it's discarded. Of course you can have sessions, to persist data between requests from the same user, and as I see there are extensions like APC, with which you can persist objects between requests at the server level.

My question is: how can one create a PHP application that works like rails and such? I mean an application that on the first requests sets up the framework, then on the 2nd and later requests use the objects that are already set up. Is there some built in caching facility in mod_php? (for example that stores the compiled bytecode of the executed php applications) Or is using APC or some similar extensions the only way to solve this problem? How would you do it?

Thanks.

EDIT: Alternative question: if I create a large PHP application that has a very large set up time, but minor running time (like in the frameworks mentioned above) then how should I "cache" the things that are already set up (this might mean a lot of things, except for maybe the database connections, because for that you have persistent connections in PHP already).

To justify large set up time: what if I'm using PHP reflection to check what objects are available and set the runtime according to that. Doing a lot of reflection is usually slow, but one has to do it only once (and re-evaluate only if the source code is modified).

EDIT2: It seems it's APC then. The fact that it caches bytecode automatically is good to know.

Tambourin answered 30/5, 2010 at 22:40 Comment(0)
E
6

Not sure if APC is the only solution but APC does take care of all your issues.

First, your script will be compiled once with APC and the bytecode is stored in memory.

If you have something taking long time to setup, you can also cache it in APC as user data. For example, I do this all the time,

            $table = @apc_fetch(TABLE_KEY);

            if (!$table) {
                    $table = new Table(); // Take long time
                    apc_store(TABLE_KEY, $table);
            }

With APC, the task of creating table is only performed once per server instance.

Ensoll answered 30/5, 2010 at 23:3 Comment(5)
Pre-compilation of the scripts is only part of the job. But if I see it corectly I have to do this part too using PHP, right? I mean for example mod_php won't do this for me.Tambourin
Code-caching is transparent. You just install APC and it will cache your byte-code automatically unless you disable it. Your app data like $table in my example will not be cached automatically. You have to do it yourself.Ensoll
Why the hell are you suppressing apc_fetch? afc_fetch returns the stored variable or array of variables on success; FALSE on failure.Cheer
@Alix Axel the script was running too fast.Turbo
Sadly support for APC in PHP5.5+ is apparently rather limited/going away: #9612176Frequently
A
4

PHP (and ruby for that matter) are interpretive languages. That is they parse the files each time they are requested and I suppose you could say are converted to a pseudo byte code. It is more 'apparent' one could say that PHP is more like this than say RoR but they both behave the same way.

The feature of persisting data between requests is a feature of the server not of the language itself. For example, the RoR routing you speak of is in fact cached but that's cached in the server's local memory. It isn't compiled and stored for faster readins. The server (and by server I mean both the box & the web service instances) restarts this information is gone. The 'setting up the framework' you speak of still involves parsing EACH file involved in the framework. Rails parses each file during the request again and again, the production level features may in fact cache this data in memory but certainly in development it does not. The only reason I mention that is because it illustrates that it's a feature of the server not the language.

To achieve the same thing in PHP you could use Zend Server. As far as I know this is the only PHP interpreter that will 'compile' and use byte code when told to. Otherwise you'll need to find a way to store the data you want to persist over requests. APC as you mentioned is a very powerful feature, a more distributed one is Memcached and then of course there's more persistent forms like disc & sql.

I am interested in knowing why you'd like this particular feature. Are you noticing performance issues that would be 'solved' by doing this?

Alberic answered 30/5, 2010 at 22:51 Comment(4)
As the edit suggests I'm using reflection to build up a lot of things (mainly to be more DRY), and it is quite slow (at least in terms of overall running time: 95% is the setup time), but it only needs to be done once. I'm just curious on how the PHP community solves "problems" like this (or actually whether they solve these "problems" at all?).Tambourin
Would you be open to discussing the particular situation you're in and maybe we can discuss 'alternative solutions'? Don't take that to mean "you're doing it wrong" I'd just like to get some more information so I can possibly direct you to the right place. If they're things that need to be setup rarely the use of a memory caching server like APC / Memcached (former for single server only installs, latter for multi server & more control).Alberic
I'm actually just curious. For the last 4 years I haven't been using PHP, and was used to how the other frameworks work. I like that they use a lot of reflection (mainly ASP.NET MVC) during the set up phase to be more DRY, and I wanted to do the same in PHP. Clearly PHP was not designed for fast reflection usage, but I don't care unless this only has to be done a few times and not for all requests.Tambourin
As PHP progresses they've started to make reflection easier. Now with 5.3 I believe you can do $class = 'ClassName';$func = 'functionName';$class->$func(args); Not that this is fast by any stretch but they have been putting effort towards reflection which is typically taken for granted in other languages. If something needs to be done once and doesn't change between requests memory storage is usually the solution for PHP apps (at least for all of mine & my companies).Alberic
M
0

I think you're making some incorrect generalizations. All of those frameworks (ex: Rails) can be run with different configurations. Under some, a process is created for every request. This obviously hurts performance, but it shows that these frameworks don't rely on a long-running process. They can set things up (reparse config files, create objects, etc.) every request if needed.

Of course, mod_php (the way PHP is usually used) runs inside the web server process, unlike CGI. So I don't see anything fundamentally different between CakePHP (for example) and Rails.

I think perhaps you are looking for something like Python's WSGI or Ruby's Rack, but for PHP. This specifies an interface (independent of how the language is run) for an application. For a new request, a new instance of an application object is created. As far as I know, this does not exist for PHP.

Misinform answered 30/5, 2010 at 22:41 Comment(4)
Yes, but they are usually not discarded and regenerated after each request. Of course you can make rails work that way, but it will also mean you have 3-4 seconds of setup time each request. And for some even if there are new processes generated they share something from the set up framework.Tambourin
3-4 seconds of setup time is a bit of a grandiose exaggeration, standard rails requests take less than 100 miliseconds on my development machines (which means RoR is running in development which means it's parsing files each and every time). Edit: You possibly have a far more complex setup than what a standard framework has so this comment is defunct.Alberic
Rails is usually run as a separate process for which the web server connects when it needs to serve another process. Passenger/mod_rails is used to handle these processes. AFAIK mod_php won't create a separate server application process (nor will it connect to it), it will just interpret the php file, and it's up to this php file to do what it needs to do. Looking at the CakePHP source codes it seems it has some caching support, but I don't know what it actually caches.Tambourin
@Matt S: rails won't reparse each file on development setup. It will only reparse modified files inside your rails application (meaning mainly the app directory). If you change some plugins, or even the main rails stuff (like active_record), it will only see the modifications after a complete recycle, which does take a lot of time. Of course during development one usually does not change the plugins or the core rails, so this isn't usually visible. And yes, loading up all the plugins and the core rails take the most time, not the actual serving of the requests.Tambourin
E
0

The most common way developers runs PHP is by configuring Apache with CGI module as an adapter in front of PHP. When Apache receives HTTP requests, verifies if a route matches to a PHP file/path, then it delegates the execution to an external module/program, in this case, the CGI module. Which will then delegate the processing to the PHP executable. A new PHP process is then started by CGI, passing request data along its parameters, and then PHP starts doing this job, which is to open PHP files, read them, validating and parsing, executing instructions line by line, until there are no more code to interpret. The response produced then is sent back to Apache and finally its process exits. Memory is returned back to OS, and then all of this pipeline happens over and over again, for every request.

Knowing this, we notice that if a process is started and terminated in every request, the state is not being preserved. Node.js for example, works by starting a single thread, read .js files, parses it, optimize it all once, and keeps itself running in memory. There is no process ending and starting (except when it crashes).

I dont know why the developers of the PHP/ Language have decided to follow this modularized approach for such a long time, they spent much effort in optimizing this architecture (it's very fast all this pipeline), but I know some companies evolved with different solutions, like Facebook, Zend.

By the way, my advice as a Senior Engineer is: Keep it simple, just apply a simple solution that everyone does, despite language/stack being used. Store application state in a dedicated space, like Redis cache, MySQL DB, etc. It's the more common solution to address the state management server applications. Applications doesnt have to depend on hold data/state in his memory space. It must be stored outside, and retrieved in every request. This brings us several benefits, including the most important in all computer programs: to share data on multiple processes running in parallel. The name of this feature is: Scalability. Write stateless applications

Engine answered 9/4, 2023 at 5:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.