Symfony remove cached images using liipImagine - php

I'm trying to remove cached images (which are created with LiipImagineBundle) when the source image is deleted or updated. I have already found out that it could be done using CacheManager ( https://github.com/liip/LiipImagineBundle/issues/132 )
The problem is that I can't figure it out how to use it exactly. What else do I need to add (like libraries) to my code despite these three lines:
$cacheManager = $this->get('liip_imagine.cache.manager');
$cacheManager->resolve($this->getRequest(),$pngPath,$filter);
$cacheManager->remove($pngPath, $filter);
I believe there should be something like
$cacheManager = new CacheManager();
I would really appreciate if anyone could explain me how to do that in more detail.

So, for example in your controller:
/**
* Remove an image in the cache based on its relative path and the filter applied to it
*
* #param string $path
* #param string $filter
*
* #return void
*/
protected function removeCachedImageAction($path, $filter)
{
$cacheManager = $this->container->get('liip_imagine.cache.manager');
// Remove the cached image corresponding to that path & filter, if it is stored
if ($cacheManager->isStored($path, $filter)) {
$cacheManager->remove($path, $filter);
}
}
/**
* An action that doesn't do much except testing the function above
*
* #param Request $request
*
* #return void
*/
protected function whateverAction(Request $request)
{
$path = //... probably from the request
$filter = //... probably from the request
// Remove the cached image
$this->removeCachedImage($path, $filter);
// ...
}
As you can see in the CacheManager, the function that you'd like to use is:
public function remove($paths = null, $filters = null){ ... }
If $paths is null, the function assumes that you want to remove the cached images for ALL PATHS that have been resolved with the $filters provided.
If $filters is null, the function assumes that you want to remove the cached images corresponding for the $paths provided and that have previously been resolved with ALL FILTERS.
If $paths and $filters are null, the function assumes that you want to remove the cached images corresponding to ALL PATHS and for ALL FILTERS. Basically ALL CACHED IMAGES.

Related

Subrequests with post vars

Here's the big picture: I am writing a symfony2 bundle for my web application. This application consists in a standard website with CRUD controllers. And on the other side it contains a rest api that also manages creation/edit/... on entities.
I started writing the Rest\UserController for the User entity. It contains all standard REST actions (GET, POST, PUT, DELETE). It is based on the very good tutorial by William Durand: http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/
Once this was created and functional I have created another UserController to handle the web side of the application. In this controller I have an action called editAction that renders a form in an HTML response. This form, when submitted sends a PUT request to the same controller's action putAction. My idea was to forward the request to Rest\UserController with action putAction. Here is the code for UserController::putAction:
/**
* This action forwards the request to the REST controller. It redirects
* to a user list upon success, or displays the message should an error
* occur.
* #Route("/{id}/put", name="igt_user_put")
* #Method("PUT")
*/
public function putAction (User $user)
{
$response = $this->forward('MyBundle:Rest\User:put', array('id'=>$user->getId()));
if ($response->getStatusCode() == Response::HTTP_NO_CONTENT) {
return new RedirectResponse($this->generateUrl('igt_user_list'));
}
return $response;
}
This works like a charm and it feels like it is the good way to do it. The problem occurred when I thought I'd do the same for user activation/deactivation. I'd have a lockAction in my UserController that would run a request through the "Rest\UserController::putAction` with synthetic data to change the enabled field.
But my problem is that there seems to be no way to set the POST vars in the forward call (only path and query). I even tried using $kernel->handle($request) with a synthetic request but it doesn't find my Rest controller's routes.
Am I missing something ?
I'm not sure of this will work or not but you could try it.
// Framework controller class
public function forward($controller, array $path = array(), array $query = array())
{
$path['_controller'] = $controller;
$subRequest = $this->container->get('request_stack')
->getCurrentRequest()->duplicate($query, null, $path);
return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
We can see that it duplicates the current request and then handles it.
// Request
/**
* Clones a request and overrides some of its parameters.
*
* #param array $query The GET parameters
* #param array $request The POST parameters
* #param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* ...
*/
public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
{
So the duplicate method will take an array of post variables. So try something like:
public function forwardPost($controller,
array $path = array(),
array $query = array(),
array $post = array())
{
$path['_controller'] = $controller;
$subRequest = $this->container->get('request_stack')
->getCurrentRequest()->duplicate($query, $post, $path);
return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
Be curious to see if this will work. I always just set my REST up as a separate application and then use guzzle to interface to it. But forwarding would be faster.

How to remove a previously added script file in a Zend Framework controller?

Is it possible to remove a script that was previously added like this?
$this->view->headScript()->appendFile("/js/my.js");
The situation here is that in my Bootstrap.php several JavaScript files are appended like in the above example but in a particular controller I want one particular JavaScript file not to be loaded. Is there a way to remove it during the initiation of the controller?
I am looking for something like this.
$this->view->headScript()->removeFile("/js/my.js");
This works, but really isn't a good practice but rather for exceptional purposes. I recommend trying to not load the unwanted scripts in the first place.
It is possible to remove scripts subsequently with a function like this one.
/**
* Removes a previously appended script file.
*
* #param string $src The source path of the script file.
* #return boolean Returns TRUE, if the removal has been a success.
*/
public function removeScript($src) {
$headScriptContainer = Zend_View_Helper_Placeholder_Registry::getRegistry()
->getContainer("Zend_View_Helper_HeadScript");
$iter = $headScriptContainer->getIterator();
$success = FALSE;
foreach ($iter as $k => $value) {
if(strpos($value->attributes["src"], $src) !== FALSE) {
$iter->offsetUnset($k);
$success = TRUE;
}
}
Zend_View_Helper_Placeholder_Registry::getRegistry()
->setContainer("Zend_View_Helper_HeadScript",$headScriptContainer);
return $success;
}
Note that the strpos function is used here. This will remove every script that has $src in its path. Of course you can change that to your needs.
It's an old question, but here is an alternative:
I used the minifyHeadScript and changed his helper adding this custom function:
/**
* Removes all script file.
*
*/
public function clearAllScripts(){
$this->getContainer()->exchangeArray(array());
$container = $this->getContainer();
Zend_View_Helper_Placeholder_Registry::getRegistry()->setContainer("Zend_View_Helper_HeadScript", $container);
}

Is there any way to compile a blade template from a string?

How can I compile a blade template from a string rather than a view file, like the code below:
<?php
$string = '<h2>{{ $name }}</h2>';
echo Blade::compile($string, array('name' => 'John Doe'));
?>
http://paste.laravel.com/ujL
I found the solution by extending BladeCompiler.
<?php namespace Laravel\Enhanced;
use Illuminate\View\Compilers\BladeCompiler as LaravelBladeCompiler;
class BladeCompiler extends LaravelBladeCompiler {
/**
* Compile blade template with passing arguments.
*
* #param string $value HTML-code including blade
* #param array $args Array of values used in blade
* #return string
*/
public function compileWiths($value, array $args = array())
{
$generated = parent::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
}
Small modification to the above script.
You can use this function inside any class without extending the BladeCompiler class.
public function bladeCompile($value, array $args = array())
{
$generated = \Blade::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
For anyone still interested in this, they've added it to Laravel 9
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
https://laravel.com/docs/9.x/blade#rendering-inline-blade-templates
I just stumbled upon the same requirement! For me, i had to fetch a blade template stored in DB & render it to send email notifications.
I did this in laravel 5.8 by kind-of Extending \Illuminate\View\View. So, basically i created the below class & named him StringBlade (I couldn't find a better name atm :/)
<?php
namespace App\Central\Libraries\Blade;
use Illuminate\Filesystem\Filesystem;
class StringBlade implements StringBladeContract
{
/**
* #var Filesystem
*/
protected $file;
/**
* #var \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
protected $viewer;
/**
* StringBlade constructor.
*
* #param Filesystem $file
*/
public function __construct(Filesystem $file)
{
$this->file = $file;
$this->viewer = view();
}
/**
* Get Blade File path.
*
* #param $bladeString
* #return bool|string
*/
protected function getBlade($bladeString)
{
$bladePath = $this->generateBladePath();
$content = \Blade::compileString($bladeString);
return $this->file->put($bladePath, $content)
? $bladePath
: false;
}
/**
* Get the rendered HTML.
*
* #param $bladeString
* #param array $data
* #return bool|string
*/
public function render($bladeString, $data = [])
{
// Put the php version of blade String to *.php temp file & returns the temp file path
$bladePath = $this->getBlade($bladeString);
if (!$bladePath) {
return false;
}
// Render the php temp file & return the HTML content
$content = $this->viewer->file($bladePath, $data)->render();
// Delete the php temp file.
$this->file->delete($bladePath);
return $content;
}
/**
* Generate a blade file path.
*
* #return string
*/
protected function generateBladePath()
{
$cachePath = rtrim(config('cache.stores.file.path'), '/');
$tempFileName = sha1('string-blade' . microtime());
$directory = "{$cachePath}/string-blades";
if (!is_dir($directory)) {
mkdir($directory, 0777);
}
return "{$directory}/{$tempFileName}.php";
}
}
As you can already see from the above, below are the steps followed:
First converted the blade string to the php equivalent using \Blade::compileString($bladeString).
Now we have to store it to a physical file. For this storage, the frameworks cache directory is used - storage/framework/cache/data/string-blades/
Now we can ask \Illuminate\View\Factory native method 'file()' to compile & render this file.
Delete the temp file immediately (In my case i didn't need to keep the php equivalent file, Probably same for you too)
And Finally i created a facade in a composer auto-loaded file for easy usage like below:
<?php
if (! function_exists('string_blade')) {
/**
* Get StringBlade Instance or returns the HTML after rendering the blade string with the given data.
*
* #param string $html
* #param array $data
* #return StringBladeContract|bool|string
*/
function string_blade(string $html, $data = [])
{
return !empty($html)
? app(StringBladeContract::class)->render($html, $data)
: app(StringBladeContract::class);
}
}
Now i can call it from anywhere like below:
<?php
$html = string_blade('<span>My Name is {{ $name }}</span>', ['name' => 'Nikhil']);
// Outputs HTML
// <span>My Name is Nikhil</span>
Hope this helps someone or at-least maybe inspires someone to re-write in a better way.
Cheers!
I'm not using blade this way but I thought that the compile method accepts only a view as argument.
Maybe you're looking for:
Blade::compileString()
It's a old question. But I found a package which makes the job easier.
Laravel Blade String Compiler renders the blade templates from the string value. Check the documentation on how to install the package.
Here is an example:
$template = '<h1>{{ $name }}</h1>'; // string blade template
return view (['template' => $template], ['name' => 'John Doe']);
Note: The package is now updated to support till Laravel 6.
I know its pretty old thread, but today also requirement is same.
Following is the way I solved this on my Laravel 5.7 (but this will work with any laravel version greater than version 5), I used the knowledge gained from this thread and few other threads to get this working (will leave links to all threads at the end, if this help up-vote those too)
I added this to my helper.php (I used this technique to add helper to my project, but you can use this function directly as well)
if (! function_exists('inline_view')) {
/**
* Get the evaluated view contents for the given blade string.
*
* #param string $view
* #param array $data
* #param array $mergeData
* #return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
function inline_view($view = null, $data = [], $mergeData = [])
{
/* Create a file with name as hash of the passed string */
$filename = hash('sha1', $view);
/* Putting it in storage/framework/views so that these files get cleared on `php artisan view:clear*/
$file_location = storage_path('framework/views/');
$filepath = storage_path('framework/views/'.$filename.'.blade.php');
/* Create file only if it doesn't exist */
if (!file_exists($filepath)) {
file_put_contents($filepath, $view);
}
/* Add storage/framework/views as a location from where view files can be picked, used in make function below */
view()->addLocation($file_location);
/* call the usual view helper to render the blade file created above */
return view($filename, $data, $mergeData);
}
}
Usage is exactly same as laravel's view() helper, only that now first parameter is the blade string
$view_string = '#if(strlen($name_html)>6)
<strong>{{ $name_html }}</strong>
#else
{{$name_html}}
#endif';
return inline_view($view_string)->with('name_html', $user->name);
return inline_view($view_string, ['name_html' => $user->name]);
References:
https://stackoverflow.com/a/31435824/4249775
https://stackoverflow.com/a/33594452/4249775
Laravel 9 :
use Illuminate\Support\Facades\Blade;
return Blade::render('Your Blade Content {{ $parameter1}}', ['parameter1' => 'Name']);

How to use Minify PHP with YUI compressor?

I would like to use YUI compressor with minify PHP rather than the default JSmin. Does anyone have experience setting this up?
Right now I am using the groupsConfig.php to combine the JS.
return array(
'jsAll' => array('//contenido/themes/bam/assets/js/jquery.js', '//contenido/themes/bam/assets/js/modernizr.js','//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/history.js','//contenido/themes/bam/assets/js/ajaxify.js', '//contenido/themes/bam/assets/js/isotope.js'),
'jsHome' => array('//contenido/themes/bam/assets/js/easing.js','//contenido/themes/bam/assets/js/scrollable.js', '//contenido/themes/bam/assets/js/home.js'),
'cssAll' => array('//contenido/themes/bam/bam.css'),
);
As it says on the homepage:
Uses an enhanced port of Douglas Crockford's JSMin library and custom classes to minify CSS and HTML
I have the following code in config.php, but I get a 500 error when trying to view the combined js file:
function yuiJs($js) {
require_once '/lib/Minify/YUICompressor.php';
Minify_YUICompressor::$jarFile = '/lib/yuicompressor-2.4.2.jar';
Minify_YUICompressor::$tempDir = '/temp';
return Minify_YUICompressor::minifyJs($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'yuiJs';
It also appears that there are several lines in lib/Minify/YUICompressor.php that need to be configured, and I'm not sure if I'm doing it right:
class Minify_YUICompressor {
/**
* Filepath of the YUI Compressor jar file. This must be set before
* calling minifyJs() or minifyCss().
*
* #var string
*/
public static $jarFile = '../yuicompressor-2.4.2.jar';
/**
* Writable temp directory. This must be set before calling minifyJs()
* or minifyCss().
*
* #var string
*/
public static $tempDir = '../../temp/';
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* #var string
*/
public static $javaExecutable = 'java';
I had the same problem on windows. It seems jar file needs to be executable in order to run yui compressor. So, i have to remove excutable check from YUICompressor.php
#132
private static function _prepare()
{
if (! is_file(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
}
// if (! is_executable(self::$jarFile)) {
// throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.');
// }
if (! is_dir(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
}
if (! is_writable(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
}
}
and that works fine.

Searching for a better way to set default values in my config class?

I reuse a Config class (simplified code at the bottom) in a lot of my projects. It's generally instantiated once in my app bootstrap file and stored in a container class from which I inject it as a dependency into objects via static "makeThis()" method calls.
Because it's instantiated every time my apps are executed I want to streamline it as much as possible. Repeat: I'm looking to make this process as efficient as possible.
I have default config values specified in the Config::$defaults array at instantiation. What I want to optimize specifically is the assignment of properties inside the load_conf_environment() function. You'll find code comments inside this function further elucidating my requirements.
The code below should be clear enough (to anyone capable of providing a valid answer) to illustrate how the class works. I'm looking for an imaginative and efficient way to handle this operation and I'm not averse to a massive refactoring if you can support your claim.
Finally, let me pre-emptively rule out one suggestion: NO, I'm not interested in using constants for the directory path values. I used to do that but it makes unit testing difficult.
UPDATE:
I've considered using gettype() to determine the default
value's type and subsequently cast the passed value, but according to
the php docs this is not a good solution.
Currently I'm storing the different config directive names in arrays
by type and doing a check to see which array contains the directive
name and perform a typecast or assign values based on that. This seems "ugly" to me
and is what I'm trying to get away from if possible.
UPDATE2:
Having config values match the variable type of the defaults is the ideal situation, but I kind of just got it into my head that I wanted it that way ... it may not even really matter. Maybe it should just be incumbent upon the coder to provide valid configuration values? Anyone have any thoughts on that?
<?php
class Config
{
/**
* The base application directory in which all app_dirs reside
*/
protected $app_path;
/**
* Configuration directives
*
* #var array
* #access protected
*/
protected $vals = array();
/**
* List of default config directive values
*
* #var array
* #access protected
*/
protected $defaults;
/**
* Class constructor -- basically sets default values
*
* #param string $app_path Base application directory
*
* #return void
*/
public function __construct($app_path=NULL)
{
$this->defaults = array(
'debug' => FALSE,
'autoload' => FALSE,
'is_cli_app' => FALSE,
'is_web_app' => FALSE,
'smarty' => FALSE,
'phpar' => FALSE,
'front_url' => '',
'app_dir_bin' => 'bin',
'app_dir_conf' => 'conf',
'app_dir_docs' => 'docs',
'app_dir_controllers' => 'controllers',
'app_dir_lib' => 'lib',
'app_dir_models' => 'models',
'app_dir_test' => 'test',
'app_dir_vendors' => 'vendors',
'app_dir_views' => 'views',
'app_dir_webroot' => 'webroot',
);
if ($app_dir) {
$this->set_app_path($app_path);
}
}
/**
* Setter function for $app_path property
*
* #param string $app_path Path to the app on the server
*
* #return void
* #throws ConfigException On unreadable/nonexistent path
*/
public function set_app_path($app_path)
{
$app_path = dirname(realpath((string)$app_path));
if (is_readable($app_path)) {
$this->app_path = $app_path;
} else {
$msg = 'Specified app path directory could not be read';
throw new ConfigException($msg);
}
}
/**
* Set default values for uninitialized directives
*
* Called after $cfg file is read and applied to fill
* out any unspecified directives.
*
* #return void
*/
protected function set_defaults()
{
foreach ($this->defaults as $key => $val) {
if ( ! isset($this->vals[$key])) {
$this->vals[$key] = $this->is_app_dir($key)
? $this->app_dir . '/' . $this->defaults[$key]
: $this->defaults[$key];
}
}
}
/**
* (Re-)Loads configuration directives
*
* #param mixed $cfg Config array or directory path to config.php
*
* #throws ConfigException On invalid config array
* #return void
*/
public function load_conf_environment($cfg)
{
// Reset all configuration directives
$this->vals = array();
if ( ! is_array($cfg)) {
$cfg = $this->get_cfg_arr_from_file($cfg);
}
if (empty($cfg)) {
$msg = 'A valid $cfg array or environment path is required for ' .
'environment configuration';
throw new ConfigException($msg);
}
foreach ($cfg as $name => $val) {
// does a setter method exist for this directive?
$method = "set_$name";
if (method_exists($this, $method)) {
$this->$method($val);
continue;
}
/*
ASSIGN SPECIFIED DIRECTIVE VALUE
THE ASSIGNED VALUE SHOULD BE OF THE SAME TYPE SPECIFIED by $this->defaults
IF DIRECTIVE IS ONE OF THE 'app_dir' VARS:
- IF IT'S A VALID ABSOLUTE PATH, SET THAT AS THE VALUE
- OTHERWISE, app_dir PATHS SHOULD BE RELATIVE TO $this->app_path
I CONSIDERED USING gettype() TO DETERMINE THE DEFAULT VAR TYPE
AND THEN CAST THE VALUE, BUT THE DOCS SAY THIS IS A BAD IDEA.
*/
}
// set default vals for any directives that weren't specified
$this->set_defaults();
if ( ! $this->vals['is_cli_app'] && ! $this->vals['is_web_app']) {
$msg = 'App must be specified as either WEB or CLI';
throw new Rasmus\ConfigException($msg);
}
}
/**
* Load a configuration array from a specified file
*
* If no valid configuration file can be found,
* an empty array is returned. Valid config paths
*
* #param string $path Config environment directory path
*
* #return array List of config key=>value pairs
* #throws ConfigException On invalid environment path
*/
protected function get_cfg_arr_from_file($path)
{
$path = (string)$path;
$path = rtrim($path, DIRECTORY_SEPARATOR) . '/config.php';
if (is_readable($path)) {
require $path;
if (is_array($cfg) && ! empty($cfg)) {
return $cfg;
}
}
return array();
}
/**
* Has the specified config directive been loaded?
*
* #param string $directive Configuration directive name
*
* #return bool On whether or not a specific directive exists
*/
public function is_loaded($directive)
{
return isset($this->vals[$directive]);
}
/**
* Retrieves a key=>value list of config directives
*
* #return array List of configuration directives
*/
public function get_directives()
{
return $this->vals;
}
/**
* Magic method mapping object properties to $vals array
*
* #param string $name Object property name
*
* #return mixed
*/
public function __get($name)
{
if (isset($this->vals[$name])) {
return $this->vals[$name];
} else {
$msg = "Invalid property: $name is not a valid configuration directive";
throw new OutOfBoundsException($msg);
}
}
}
?>
Handling default values
Is there a reason why you set defaults for directives that need them after loading values from the config file?
Since the defaults are already hard-coded in the __construct, why not just move them to the $vals property and avoid needing to set them later on? This way, your load_conf_environment method overwrites the default value that already exists for the directive.
Validating types
This is something I would enforce with each directive's setter method. Each setter should verify the type of value with the is_* functions or by type-hinting in the setter arguments and throw exceptions accordingly. I would probably go so far as to require each directive have a setter. It's more work, but cleaner and 100% enforceable.
Handling app_dir directives (one option)
In the construct, if set_app_path is called, run through the vals property and call the set_app_dir_directive method (see below) on the app_dir directives so that they are prefixed with the app path.
Make a new set_app_dir_directive method:
public function set_app_dir_directive($key, $val)
{
$this->vals[$key] = $this->app_dir . '/' . $val;
}
// then...
public function load_conf_environment($cfg)
{
...
foreach ($cfg as $name => $val) {
$method = "set_$name";
if (method_exists($this, $method)) {
...
}
if ($this->is_app_dir_directive($name)) {
$this->set_app_dir_directive($name, $val);
continue;
}
// Continue storing vals.
...
}
}

Categories