Our LegacyApp uses built-in PHP
$_SESSION facility to store a number of values for user: ID of currently logged-in client and/or admin, hashes of their passwords, shopping cart, value for anti-CSRF token generation, etc. We would certainly want to have access to these values from Symfony code.
Symfony 2, on the other hand, has
Session class, which is a part of its HttpFoundation component. Symfony session implementation abstracts out session storage, and makes session a part of a
Request object. It has built-it support for flash attributes (auto-deleting after access) and other goodies.
Session interoperable requires us to:
- Make sure
Session work with same session which is initialized and started in a uniform way;
- Make sure
Session APIs are able to access same values in session.
The first part requires us to decide whether we want session to be started by Symfony or by legacy code. Legacy part of our app probably sets up its session with
session_*() calls. At the very least it likely calls
session_name() to set session cookie name, and then
Continue reading “Symfony and Legacy Code: Interface with legacy $_SESSION”
Symfony Dependency Injection container is perhaps my favorite component of Symfony 2.
It is a non-trivial component to understand, though, with its multitudes of YAML/XML files and weird classes in
DependencyInjection namespaces and bundle semantic configuration and whatnot. In my first Symfony 2 project, I was thoroughly confused by Dependency Injection (along with Doctrine’s data mapper semantics) and ended up treating classes in
DependencyInjection namespaces and YAML configs as magic incantations. I began to realize what DI was all about only after I wrote a sizable application based on Silex with its trivial Pimple container. And for a large application like our LegacyApp, DI is simply indispensable.
In my Symfony code, I define as much classes as I can as services. It is not strictly necessary for controllers, console commands, and Doctrine repositories (and dependency-injected constructors for controllers may get quite daunting), but I find it very helpful to think of services as basic building blocks of an application. Apart from a few Symfony-specific classes like
Bundle subclasses, everything is either a service, or created and managed by services. And of course dependencies on
ContainerInterface are a no-no. We want to use container to wire up components of our application on startup, not to use it as as “service locator” to retrieve random services whenever we feel like it.
Apart from consistency, a great reason to use Symfony DI is that it is fairly simple and clean to call services from legacy code. We have already looked at some examples of calling legacy code from Symfony layer, but in real life, the opposite is often necessary as well. For example, we may have just refactored some legacy
invoicegeneration.php code with a bunch of functions to a shiny new
InvoiceGenerator class, registered via DI as an
invoicing.generator service. We still want to keep
invoicegeneration.php and its functions with their APIs, though; they just need to be wrappers for
InvoiceGenerator methods now. How can we do that?
Turns out, as far as interfacing with legacy code goes, this is mostly trivial.
Continue reading “Symfony and Legacy Code: Calling from legacy code to Symfony via DI container”
We have already examined how to wrap an arbitrary legacy URL in our Symfony app. Turns out, many apps also have some CLI (console) scripts, used to run cron jobs, or for some low-level configuration, or whatever. What are we going to do with them?
Well, pretty much exactly what we did with URLs: instead of a catch-all
LegacyController, write a catch-all
LegacyCommand. This is going to be a bit easier as we do not need to mess with HTTP headers and responses for this.
Continue reading “Symfony and Legacy Code: Wrapping legacy console scripts”
We can now call into legacy code from new Symfony-based code with ease. That will come useful for replacing old LegacyApp pages or creating entirely new ones. But what about pages which we don’t want to touch for now?
For that we should write a
LegacyController class, which will handle all requests like
GET /path/to/legacy/script.php, and render output of that script; that approach was briefly mentioned in the previous post. In this way, URLs won’t change, and legacy links won’t break.
Continue reading “Symfony and Legacy Code: Wrapping legacy URLs”
Having stated our premise in previous post, let’s have a look at actually setting up a Symfony project capable of interoperating with LegacyApp.
Our big goal is to make LegacyApp a part of larger Symfony-based application. It should be wrapped entirely in Symfony code, and its URL and CLI scripts launched only from Symfony front controllers.
Why? This is, in practical terms, the only viable choice there is. We don’t want Symfony and legacy apps to be entirely separate and working side by side. Most of application features depend on each other in various ways, and if the only way new and old code can interact is through database, this is going to be tricky.
Another theoretical option is to keep LegacyApp a first-class citizen and make it call into Symfony code for new code paths. This is very inconvenient as:
- We lose benefits of having a single front controller in application
- We lose ability to do useful things in global event listeners like
- We lose ability to have our routing managed by Symfony
- We probably will not use Symfony-based controllers at all, keeping “one controller = one .php script” approach
- Depending on how our LegacyApp bootstrap code works, it might be difficult to make sure Symfony is initialized at a given point of our code at all
Continue reading “Symfony and Legacy Code: Calling from Symfony into legacy code”
There are billions of lines of horrible code in the world. There are billions (I think) of lines of PHP code in the world. These two sets have significant overlap.
PHP, as of 2014, remains the most widely used web development language, particularly for applications which do not warrant use of Java or .NET or something else “serious”. At the same time, PHP is widely considered one of the worst languages there is. Now, “worst”, I believe, is an overstatement; by itself PHP is merely mediocre. But its low barrier for entry, together with steadily high demand for web development jobs, resulted in proliferation of messy PHP code.
Now, it is certainly possible to write good PHP. Symfony 2, the best, in my opinion, PHP web framework at the moment, is a fine example. PHP written in Symfony style tends to look a lot like Java. Still not glamorous, but much easier and more pleasant to work with.
The sad truth is, most of PHP code in the world is not written with Symfony, nor it looks even remotely like Symfony code. So, what if, by some chance, you have been entrusted with maintenance of a huge legacy PHP codebase?
Continue reading “Symfony and Legacy Code: Introduction”