-
Notifications
You must be signed in to change notification settings - Fork 201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Glide 2.0 or future development #288
Comments
Great question @rreynier! I am excited to announce that, as of today, @tgalopin has officially taken over as the core maintainer of this project. 🙌 As I announced on Twitter, as much as I love it, I simply don't have the capacity for this project any longer. Passing this project off to Titouan felt like the right thing to do for Glide. 👍 Titouan actually reached out to me months ago, showing interest in this project, and sharing some vision he has for it, which is really cool! That all said, I'll probably be around a little still during the transition phase...helping Titouan as needed. I think Titouan is planning to get into the project in the next month or two. |
I profit from this issue and the fact that I (finally) have some time to dedicate to Glide to explain what my ideas are. Warning: long answer :D Note that these ideas are what I propose but I'm fully open to suggestions too, I see open-source as community-driven: if you have a need and open a PR, my default opinion will be to accept it unless there is a good reason not to. In the short term, there are two things I'm working on:
This roadmap will 1/ help people using 1.0 to keep using it (with necessary features) for a while, but at the same time 2/ provide an easy migration path to Flysystem 2.0 as Glide 2.0 will only focus on this upgrade, thus limiting the struggle to upgrade, and finally 3/ in 3.0 we will be able to go further in terms of BC breaks. Glide 3.0For Glide 3.0, I proposed several ideas to Jonathan a while ago that still applies today IMO. I would like to rethink the library developer experience to ease its usage, simplify its code and improve its capabilities. Here is how it could look like in terms of usage: $glide = Glide::create([
// Flysystem, source for the images data
// Required
'source' => $source,
// For the cache, I would like to use Symfony Cache instead, for reasons detailed below
// Required
'cache' => $cache,
// The response factory to use (default to HttpFoundation, as it's the most common one)
'response_factory' => $responseFactory,
// Equivalent of the "API" in Glide 1.0, but I think this naming is clearer
// This object will dispatch the manipulation to an array of manipulators
// If not provided, creates a default one
'image_manipulator' => $imageManipulator,
// Secret used to sign image requests, if null signing is disabled
'secret' => 'af3dc18fc6bfb2afb521e587c348b904',
// Maximum image size that Glide will accept to render, null to have no limit
'max_image_size' => 2000*2000,
// Default parameters for image storage handling
'default_storage_parameters' => [
'w' => 2000,
'h' => 2000,
'fit' => 'contain',
'fm' => 'pjpg',
'q' => 80,
],
// Presets usable in parameters (?p=user-picture-small)
'presets' => [
'user-picture-small' => [
'w' => 200,
'h' => 200,
'fit' => 'crop',
],
],
]);
// Apply initial manipulations to an image and store it in the source at a given path
// Here, the image is resized and re-encoded at storage to decrease the stored file size
// Easier to understand/manipulate than the source Filesystem directly (still possible of course)
$glide->store('users/'.$username.'.jpeg', file_get_contents($uploadedFile), [
'w' => 2000,
'h' => 2000,
'fit' => 'contain',
'fm' => 'pjpg',
'q' => 80,
]);
// Check whether the given path exists in the source filesystem
$glide->has('users/'.$username.'.jpeg');
// Remove an image from the source filesystem
$glide->remove('users/'.$username.'.jpeg');
// Clear the cache for a given path
$glide->clearCache('users/'.$username.'.jpeg');
// Clear the cache for all paths
$glide->clearAllCache();
// Sign a given request path and parameters and return the signature
// Returns an empty string if no secret was provided as configuration
$glide->sign('users/'.$username.'.jpeg', [
'w' => 200,
'h' => 200,
'fit' => 'crop',
]);
// Handles a given request with parameters and return a Response using the ResponseFactory
// Throws a NotFoundException if the image doesn't exist or the signature is not valid
// Throws a BadRequestException if the image size is too large or the parameters are not supported
// Exceptions are thrown by the ResponseFactory to profit from framework exceptions
$glide->handle('users/'.$username.'.jpeg', [
'w' => 200,
'h' => 200,
'fit' => 'crop',
's' => '9978a40f1fc75fa64ac92ea9baf16ff3',
]);
// Directly display an image by sending appropriate headers and content through native PHP calls
// Throws a NotFoundException if the image doesn't exist or the signature is not valid
// Throws a BadRequestException if the image size is too large of the parameters are not supported
// Exceptions are thrown by the ResponseFactory to profit from framework exceptions
$glide->display('users/'.$username.'.jpeg', [
'w' => 200,
'h' => 200,
'fit' => 'crop',
's' => '9978a40f1fc75fa64ac92ea9baf16ff3',
]);
// Create the base 64 representation of an image
// Throws a NotFoundException if the image doesn't exist or the signature is not valid
// Throws a BadRequestException if the image size is too large of the parameters are not supported
// Exceptions are thrown by the ResponseFactory to profit from framework exceptions
$glide->createBase64('users/'.$username.'.jpeg', [
'w' => 200,
'h' => 200,
'fit' => 'crop',
's' => '9978a40f1fc75fa64ac92ea9baf16ff3',
]); These changes would make Glide even more straightforward IMO. Besides the API changes, there are 2 main updates:
Using Symfony Cache has many advantages, the main one being the way it's designed internally. The CacheInterface defines only two methods:
With this structure, a typical cache fetching would look like the following: $value = $cache->get('my-key', function(ItemInterface $item) {
$item->expiresAfter(3600);
// compute the value ...
return $computedValue;
}); There are two execution paths possible here:
This interface, in addition to being elegant in terms of DX, provides by default a huge advantage over other cache interfaces: it's extremely easy to add locking features around cache generation, avoiding multiple frontends to compute the same value at the same time. That's what the Symfony Cache component does by default: it uses the caching storage to store a lock of currently computed values and avoid multiple frontends taking the CPU/memory hit of computing the cache. In our context, this is a huge advantage: computing multiple images is CPU-intensive and having multiple PHP-FPM workers computing the same cache on the same time is wasting resources. The extra parameter, Locking is a great way to avoid multiple frontends to compute the same values but these frontends will still be blocked, waiting for the cache to be ready. Having multiple frontends blocked by a cache generation can be a big problem as it uses a network connection and a PHP-FPM worker. Probabilistic expiration solves this by calculating an increasing probability to expire before the actual expiration date. This means that the cache will randomly be computed earlier than the configured expiration date, this time by a single frontend, and with a probability of doing so that increases as we approach the configured expiration date. This avoids to get all frontends to compute the cache at the same time. By using Symfony Cache, Glide would profit from these powerful features without having to do anything, thus my proposal. Big answer, but I thought it was interesting to expose my ideas :) . Feel free to send feedbacks! |
Linking the previous Glide 2.0 brainstorming issue #170 here for better visibility. "Serve cached images via web server" is one feature I would like to be realized. |
Absolutely, very good idea IMO. It's going to be interesting to see how we can do that. |
I think you can already do something like this: Path like: /media/something/jumbotron.jpg/glide/w/600/h/300/jumbotron3.jpg <?php
$sourceFolder = '/var/www/public/media';
$cacheFolder = __DIR__ .'/../storage/cache';
require __DIR__ . '/../vendor/autoload.php';
// Split on the /glide/
list($path, $params) = explode('/glide/', $_SERVER["REQUEST_URI"]);
$params = explode('/', $params);
$filename = array_pop($params);
// Move the url into key/value pairs
$result = [];
while (count($params) >= 2) {
list($key, $value) = array_splice($params, 0, 2);
$result[$key] = $value;
}
// Setup Glide server
$server = League\Glide\ServerFactory::create([
'source' => $sourceFolder,
'cache' => $cacheFolder,
]);
// Store for direct access later
$source = $cacheFolder .'/'. $server->makeImage($path, $result);
$target = __DIR__ .'/../public/' . $_SERVER["REQUEST_URI"];
mkdir(dirname($target), 0777, true);
copy($source, $target);
// Serve for now
$server->outputImage($path, $result); (Use the directories as prefix) Downside is that you can't do auto content-negotiation (WebP) |
Are there plans to continue development on Glide 2.0? It seems like the project is essentially in maintenance mode (which is fine). Just curious what the long term plans are for the project moving forward.
The text was updated successfully, but these errors were encountered: