Symfony - fallback to another application if Symfonfy app can not handle the request - yii

We have an old Yii application along with new Symfony one.
The basic idea is simple - I need to check if there is a route matching in Symfony application then it is cool, if not then bootstrap Yii application and try to handle the request with it.
The main idea to not instantiate AppKernel (and do not load autoload.php - since there is two different autoload.php for each project) before I am sure there is route matching.
Can I do it somehow?

We've done this before with legacy applications.
There are two approaches you can take.
Wrap your old application inside a symfony project (recommended).
Unfortunately this will indeed load the symfony front-controller and kernel. No way around that. You need to make sure that symfony can't handle the request and to do that the kernel needs to be booted up.
Use sub-directories and apache virtual hosts to load one application vs the other as needed.
Given option 1,
You can either create your own front controller that loads either symfony or yii by reading routes (from static files if using yml or xml, or annotations which will be more complex) OR EventListener (RequestListener) that listens to the HttpKernelInterface::MASTER_REQUEST and ensures that a route can be returned.
Creating your own front controller is the only way that you can make it not load the symfony kernel, but it will require you to write something that understands the routes in both frameworks (or at least symfony's) and hands off the request appropriately.
Event listener example:
public function onkernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
... Code to continue normally, or bootstrap yii and return a custom response... (Can include and ob_start, or make an http request, etc)
}
public function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest']
];
}
As you see, the kernel needs to be booted to ensure symfony can't serve the route. Unless creating your own front controller (as stated above).
A third approach would be to create a fallback controller, which would load up a specified URL if no route was found within symfony. Although this approach is generally used for legacy projects that lack a framework and use page scripts instead of proper routes, and definitely requires the use/help of output buffering.
The EventListener approach gives you the opportunity to create a proper Request to hand off to yii, and using what is returned to create a Response as proper symfony object (can also use ob or other options).
Thank you.

This is an alternative to vpassapera's solution -http://stovepipe.systems/post/migrating-your-project-to-symfony

Related

Workbox/Vue: Create a custom variation on an existing caching strategy handler

Background:
I'm building an SPA (Single Page Application) PWA (Progressive Web App) using Vue.js. I've a remote PostgreSQL database, serving the tables over HTTP with PostgREST. I've a working Workbox Service Worker and IndexedDB, which hold a local copy of the database tables. I've also registered some routes in my service-worker.js; everything is fine this far....
I'm letting Workbox cache GET calls that return tables from the REST service. For example:
https://www.example.com/api/customers will return a json object of the customers.
workbox.routing.registerRoute('https://www.example.com/api/customers', workbox.strategies.staleWhileRevalidate())
At this point, I need Workbox to do the stale-while-revalidate pattern, but to:
Not use a cache, but instead return the local version of this table, which I have stored in IndexedDB. (the cache part)
Make the REST call, and update the local version, if it has changed. (the network part)
I'm almost certain that there is no configurable option for this in this workbox strategy. So I would write the code for this, which should be fairly simple. The retrieval of the cache is simply to return the contents of the requested table from IndexedDB. For the update part, I'm thinking to add a data revision number to compare against. And thus decide if I need to update the local database.
Anyway, we're now zooming in on the actual question:
Question:
Is this actually a good way to use Workbox Routes/Caching, or am I now misusing the technology because I use IndexedDB as the cache?
and
How can I make my own version of the StaleWhileRevalidate strategy? I would be happy to understand how to simply make a copy of the existing Workbox version and be able to import it and use it in my Vue.js Service Worker. From there I can make my own necessary code changes.
To make this question a bit easier to answer, these are the underlying subquestions:
First of all, the StaleWhileRevalidate.ts (see link below) is a .ts (TypeScript?) file. Can (should) I simply import this as a module? I propably can. but then I get errors:
When I to import my custom CustomStaleWhileRevalidate.ts in my main.js, I get errors on all of the current import statements because (of course) the workbox-core/_private/ directory doesn't exist.
How to approach this?
This is the current implementation on Github:
https://github.com/GoogleChrome/workbox/blob/master/packages/workbox-strategies/src/StaleWhileRevalidate.ts
I don't think using the built-in StaleWhileRevalidate strategy is the right approach here. It might be possible to do what you're describing using StaleWhileRevalidate along with a number of custom plugin callbacks to override the default behavior... but honestly, you'd end up changing so much via plugins that starting from scratch would make more sense.
What I'd recommend that you do instead is to write a custom handlerCallback function that implements exactly the logic you want, and returns a Response.
// Your full logic goes here.
async function myCustomHandler({event, request}) {
event.waitUntil((() => {
const idbStuff = ...;
const networkResponse = await fetch(...);
// Some IDB operation go here.
return finalResponse;
})());
}
workbox.routing.registerRoute(
'https://www.example.com/api/customers',
myCustomHandler
);
You could do this without Workbox as well, but if you're using Workbox to handle some of your unrelated caching needs, it's probably easiest to also register this logic via a Workbox route.

How can I run Zend Framework code alongside legacy (non-ZF) code on the same server on the same HTTP port?

I have a large codebase that I am trying to eventually convert to Zend-Framework-powered stack.
I at times write new modules to where I have a choice:
keep writing using legacy routing/initialization/etc
somehow figure out how to use ZF for the new module only while the rest of the legacy code works "as before"
Is this possible?
How?
To give you an idea, code I have now uses proprietary multiple routing files, where everything in ZF goes through one single router file.
So legacy code is called like so i.e.:
http://legacy:80/index.php?route=product
May be similar to zend framework 2 in a subdirectory
Zend Middleware approach
I was able to follow https://docs.zendframework.com/zend-mvc/middleware/ and implement an IndexMiddleware class. I can see that IndexMiddleware::process() method is being called. But I am not certain how to go further, and how to engage my legacy web application to return data as before.
MiddlewareListener.
Legacy App - index.php
$module = filter($_GET['p']);
if (!empty($module))
$inc = 'portal/{$module}.php'; //prep a legacy module
require($inc); //run module
There are many solutions there... Depends on how much new code you have, and addresses you want.
Long story short, you could work at the server level (aliases, rewrite, etc), or at the PHP code level.
Something you could do is use the index.php from the Zend Skeleton for instance, and the default url routing through index.php. Then look at the application lifecycle, especially the route event. I believe that's a good point to add a listener that would dispatch the old application. You can find numbers of Listeners in the Zend MVC code to base your code on (look at the middleware one for instance).

Adding custom route to Zend REST controller

I am using Zend F/W 1.12 in order to build a REST server.
One of my requirements are to have an action that Is outside the boundaries of what Zend can recognize as a "Restfull" action. What I mean is that I would like to have an action that is called something like mymedia and would like tou routes requests that are directed to //mymedia . Currently, Zend understand it as the id to a getAction and off course this is not what I want.
Any help will be highly appreciated!
Thanks
The implementation of Zend_Rest_Route does not allow much customization but instead provides a rudimental routing scheme for out-of-the-box usage.
So if you need to change the way how URIs are interpreted you can extend Zend_Rest_Route, Zend_Controller_Router_Route_Module or Zend_Controller_Router_Route_Abstract class to create your own kind of routing.
Have a look at the match method of those classes and what they do - e.g. they populate the $_values property array (while respecting the $_moduleKey, $_controllerKey and $_actionKey properties).
You can then add it e.g. as the first route within your bootstrap class:
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$router->addRoute('myRoute', new My_Route($frontController));
$router->addRoute('restRoute', new Zend_Rest_Route($frontController));
See:
http://framework.zend.com/manual/1.12/en/zend.controller.router.html#zend.controller.router.basic
Routing is a simple process of iterating through all provided routes and matching its definitions to current request URI. When a positive match is found, variable values are returned from the Route instance and are injected into the Zend_Controller_Request object for later use in the dispatcher as well as in user created controllers. On a negative match result, the next route in the chain is checked.
I once wrote a custom route for zend framework 1 that can handle custom restful routes. it served me well until now. see https://github.com/aporat/Application_Rest_Controller_Route for more details.
for example, if you want to have a url such as /users/30/messages mapped correctly into a zend controller action, use this route in your bootstrap:
$frontController = Zend_Controller_Front::getInstance();
$frontController->getRouter()->addRoute('users-messages', new Application_Rest_Controller_Route($frontController, 'users/:user_id/messages', ['controller' => 'users-messages']));

AngularJS dynamic application with or without routing

My application has 2 purposes:
It needs to run stand-alone, where it needs routing for choosing a
study etc.
Or, it runs integrated in an other project, and only needs
one controller and one view.
Currently i have a routeProvider configured for the stand-alone application, injecting the pages in the ng-view tag in the HTML.
Now is my question: How can i inject an controller and view in the ng-view (For the integration). I cannot manipulate the HTML since it is static. I cant use a single routeProvider rule, because this can interfeir the application that integrates mine (Other plugins can use the #/.. for info or other things).
In your situation you can't use routeProvider when other stuff interferes.
Of Course you could prevent routeProvider to act on outside changes of the hashbang with workarounds but thats not nice.
routeProvider will listen to all changes of the url after the hashbang.
So what you should do is to manually bootstrap() your angular app with the controllers you need. If your app is small enough you could even use directives to achieve lazy loading of templates with the attribute templateUrl : "/myurl"
Usually to create a dynamic App use Routing. Simnple point.
The best way to use Angular if you want to unleash all its might don't integrate it.
I explain why:
+ Your state never gets lost due to page reloads
+ You have full control of the environment and don't have to worry about interfering scripts etc.
+ If your user should manually reload, you can redirect to home/login or even better use requireJS or HTML5 local storage to recover your scopes after a reload
Cheers, Heinrich

Codeigniter deny access to directory of controllers with apache config

I’d like to restrict access to a folder of controllers that are used for admin purposes only. I’ve tried a number of ways and not coming up with a solution. These controllers are behind password protection. But, I’d like to just remove it from view if someone happens to stumble upon the right directory. Can this be done? I’d rather not do it from htaccess. I have access to the apache config files, so I’d like to handle it there.
Does it have anything to do with the way Codeigniter routes? Or, am I just way off?
This what I’m using that doesn’t work
<Directory /var/www/application/controllers/folder/>
Order deny,allow
Deny from all
Allow from xxx.xxx.xxx.xxx
</Directory>
Due to the way we re-write urls to work with CI, you'd never match your Apache config because you're actually requesting index.php?{args}. If you want to filter, you have to do it in CI instead. Your options are a core controller or hooks.
A simple way to do it is to create a core controller that your admin/ area scripts extend, and check the IP there.
Something like this:
application/core/MY_Controller.php:
class MY_Controller extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->config('permitted_ips');
// check visitor IP against $config['ips'] array, redirect as needed
}
}
Then, in your 'sensitive' controllers, extend MY_Controller:
application/controllers/admin/seekrit.php
class Seekrit extends MY_Controller
{
public function __construct() {
parent::__construct();
/* at this point any invalid IP has been redirected */
}
}
Now, if you're already using a core controller for something else, just check $this->uri->segment() to see if they're in a restricted area before loading the allowed IP configuration file and checking / redirecting / dying or whatever else you need to do.
Also, there's no need to use a constructor in your admin controllers if you don't need one, as the parent will be constructed if one is not defined. Just be sure to call the parent if you define one.
You could also put the white list in a database, Redis, whatever.
Another way to do this would be by using hooks, specifically the pre_controller hook. By the time that hook is entered, all of the security and base classes have run. This would be appropriate if you wanted to protect some or all of your routes in a more granular fashion. There, you could define an array containing routes, such as:
$protected_routes = array(
'foo' => array(
'allow_ip' => '1.2.3.4',
'redirect_if_not' => site_url('goaway')
)
)
Then, in your hook class (or function) match the first segment (my example is just a function):
$CI = get_instance();
$CI->load-config('my_hook');
$protected_routes = $CI->config->item('protected_routes');
$segment = $CI->uri->segment(1); // foo
if (in_array($segment, $protected_routes)) {
// grab $protected_routes[$segment] and work with it
}
This has the advantage of not cluttering up your core controller as many people use that as a means of sharing code between methods. However, the hook will run on every request which means adding another two file loads to bootstrap.
I used the hook method on a large RESTful service to protect certain endpoints by requiring additional headers, and enforcing different kinds of rate limiting on others. Note, the code above is just an example of what could go in the hook, not how to set up the hook itself. Read the hooks section of the CI manual, it's extremely easy and straight forward.
Finally, if you really want to do it via .htaccess, you'll have to go by the request itself. The directory application/controllers/foo is never entered, the actual request is /foo/controller/method{args}, which causes CI to instantiate the foo/controller.php class. Remember, once re-written, the server sees index.php?....
To accomplish this, you can re-write based on the request URI pattern, something like this (have not tested, YMMV):
RewriteRule (^|/)foo(/|$) - [F,L]
Which can be used to redirect anyone accessing the virtual path to your protected controllers. This could be preferable as it prevents PHP from needing to handle it, but you lose the granularity of control over what happens when there is a match. Still, you could use something like the above re-write combined with a hook or core implementation if you have more than one sensitive area to protect.
Tim Post's idea above is similar to another method I either saw on this site or somewhere else. It took me awhile to get back to this issue, but at long last it's done.
As TheShiftExchange pointed out in the comments under my original question, .htaccess will not work for a Codeigniter project. Below is what I ultimately ended up with and seems to work well. It probably isn't 100% secure, but I really just wanted to remove these pages from being directly accessed. If someone were to manage to get to the page there is still a user/pass login screen.
New Config File in application/config
switch (ENVIRONMENT) //set in index.php
{
case 'development':
$config['admin_ips'] = array('XXX.XXX.XXX.XXX');
break;
case 'testing':
$config['admin_ips'] = array('XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX');
break;
case 'production':
$config['admin_ips'] = array('XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX');
break;
}
New Controller
class Admin_IP_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
$this->load->config('admin_ips');
if (!in_array($this->input->ip_address(), $this->config->item('admin_ips')))
{
show_404();
}
}
}