Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixes bug in related to SoftDelete behavior in preDqlDelete, closes #17

More coverage tests.

Adds another approach to add custom cache key
parameters for authenticated users.

Fixes minor bug with non-unique invoker namespace
names in Cachetaggable template.

Updates README file with information how to use
"cache.filter_cache_keys" event.
  • Loading branch information...
commit ae2c1233f6aff5d8700559e096f24aab755f103e 1 parent 33e30a5
@fruit authored
Showing with 1,773 additions and 869 deletions.
  1. +4 −0 .gitignore
  2. +93 −75 README.markdown
  3. +1 −0  VERSION
  4. +1 −5 config/sfCacheTaggingPluginConfiguration.class.php
  5. +1 −1  lib/cache/extended/sfSQLitePDOTaggingCache.class.php
  6. +32 −14 lib/cache/sfTaggingCache.class.php
  7. +30 −10 lib/filter/AuthParamFilter.class.php
  8. +24 −3 lib/log/sfCacheTagLogger.class.php
  9. +16 −14 lib/log/sfFileCacheTagLogger.class.php
  10. +30 −0 lib/log/sfOutputCacheTagLogger.class.php
  11. +2 −0  lib/request/sfCacheTaggingWebRequest.class.php
  12. +28 −24 lib/util/sfCacheTaggingToolkit.class.php
  13. +3 −2 lib/util/sfTagNamespacedParameterHolder.class.php
  14. +12 −15 lib/vendor/Doctrine/Connection/CachetaggableUnitOfWork.php
  15. +17 −27 lib/vendor/Doctrine/Template/Cachetaggable.php
  16. +63 −36 lib/vendor/Doctrine/Template/Listener/Cachetaggable.php
  17. +181 −120 lib/view/sfViewCacheTagManager.class.php
  18. +80 −59 package.xml
  19. +2 −1  test/bootstrap/functional.php
  20. +6 −0 test/fixtures/project/apps/frontend/config/factories.yml
  21. +3 −0  test/fixtures/project/apps/frontend/config/filters.yml
  22. +1 −11 test/fixtures/project/apps/frontend/config/frontendConfiguration.class.php
  23. +4 −3 test/fixtures/project/apps/frontend/config/settings.yml
  24. +1 −1  test/fixtures/project/apps/frontend/config/view.yml
  25. +32 −3 test/fixtures/project/apps/frontend/lib/myUser.class.php
  26. +59 −4 test/fixtures/project/apps/frontend/modules/blog_post/actions/actions.class.php
  27. +17 −1 test/fixtures/project/apps/frontend/modules/blog_post/config/cache.yml
  28. +5 −0 test/fixtures/project/apps/frontend/modules/blog_post/config/security.yml
  29. +17 −0 test/fixtures/project/apps/frontend/modules/blog_post/templates/actionWithAutoSignInSuccess.php
  30. +1 −0  test/fixtures/project/apps/frontend/modules/blog_post/templates/signInError.php
  31. +1 −0  test/fixtures/project/apps/frontend/modules/blog_post/templates/welcomeSuccess.php
  32. +3 −5 test/fixtures/project/config/ProjectConfiguration.class.php
  33. +0 −1  test/fixtures/project/config/doctrine/blog_post.yml
  34. +0 −1  test/fixtures/project/config/doctrine/blog_post_comment.yml
  35. +30 −0 test/fixtures/project/config/doctrine/blog_post_vote.yml
  36. +0 −1  test/fixtures/project/config/doctrine/book.yml
  37. +26 −0 test/fixtures/project/config/doctrine/food.yml
  38. +0 −51 test/fixtures/project/config/doctrine/food_manufacturer_reordered.yml
  39. +26 −0 test/fixtures/project/config/doctrine/food_reordered.yml
  40. +0 −23 test/fixtures/project/config/doctrine/{food_manufacturer.yml → manufacturer.yml}
  41. +28 −0 test/fixtures/project/config/doctrine/manufacturer_reordered.yml
  42. +15 −2 test/fixtures/project/config/doctrine/relations.yml
  43. +0 −1  test/fixtures/project/config/doctrine/repository.yml
  44. +0 −1  test/fixtures/project/config/doctrine/university.yml
  45. +13 −1 test/fixtures/project/config/factories.yml
  46. +29 −26 test/fixtures/project/data/fixtures/cascade.yml
  47. +1 −0  test/fixtures/project/data/sessions/.gitignore
  48. +9 −0 test/fixtures/project/phpweb
  49. +1 −1  test/fixtures/project/symfony
  50. +0 −6 test/fixtures/project/web/frontend_dev.php
  51. +0 −7 test/fixtures/project/web/frontend_test.php
  52. +10 −0 test/fixtures/project/web/index.php
  53. +0 −6 test/fixtures/project/web/notag_dev.php
  54. +0 −6 test/fixtures/project/web/notag_test.php
  55. +180 −0 test/functional/frontend/AuthParametersTest.php
  56. +217 −0 test/functional/frontend/CachetaggableAndSoftDeleteBehaviorsTest.php
  57. +132 −88 test/functional/frontend/CascadeDeleteOrInvalidateTest.php
  58. +39 −131 test/functional/frontend/DoctrineListenerCachetaggableTest.php
  59. +4 −2 test/functional/frontend/DoctrineNestedSetBehaviorTest.php
  60. +4 −0 test/functional/frontend/DoctrineRecordLinkAndUnlinkTest.php
  61. +49 −0 test/functional/frontend/actionAuthParamFilterTest.php
  62. +32 −28 test/functional/frontend/browserViewCacheTest.php
  63. +36 −0 test/functional/frontend/sfCacheTaggingWebRequestTest.php
  64. +1 −2  test/functional/frontend/sfViewCacheTagManagerBridgeTest.php
  65. +1 −1  test/functional/frontend/sfViewCacheTagManagerTest.php
  66. +2 −34 test/functional/notag/sfCacheTaggingToolkitTest.php
  67. +3 −3 test/unit/sfCacheTagLoggerTest.php
  68. +44 −5 test/unit/sfCacheTaggingToolkitTest.php
  69. +2 −2 test/unit/sfFileCacheTagLoggerTest.php
  70. +31 −0 test/unit/sfOutputCacheTagLoggerTest.php
  71. +0 −2  test/unit/sfTaggingCacheInterfaceTest.php
  72. +38 −3 test/unit/sfTaggingCacheTest.php
View
4 .gitignore
@@ -5,6 +5,10 @@ README
/test/fixtures/project/cache/*
/test/fixtures/project/log/*
/test/fixtures/project/data/sql/*
+/test/fixtures/project/data/sessions/*
/test/fixtures/project/data/sqlite/*
/test/fixtures/project/lib/model/*
!.gitignore
+/test/fixtures/project/web/sf
+/test/fixtures/project/web/sfDoctrinePlugin
+php_errors.log
View
168 README.markdown
@@ -1,32 +1,33 @@
# sfCacheTaggingPlugin
-The ``sfCacheTaggingPlugin`` is a ``Symfony`` plugin, that helps to store cache with
-associated tags and to keep cache content up-to-date based by incrementing tag
-version when cache objects are edited/removed or new objects are ready to be a
-part of cache content.
+The sfCacheTaggingPlugin is a Symfony plugin that allows you to not think about
+cache obsolescence. The user will see only a fresh data thanks to cache tagging.
+The cache will be linked with a tags versions and will be incremented when the
+cached Doctrine objects were edited/removed or new Doctrine objects are
+ready to be a part of cache content.
# Table of contents
* <a href="#desc">Description</a>
- * <a href="#install">Installation</a>
+ * <a href="#installation">Installation</a>
* <a href="#quick-setup">Quick setup</a>
* <a href="#usage">Usage</a>
* <a href="#advanced-setup">Advanced setup</a>
- * <a href="#misc">Miscellaneous</a>
+ * <a href="#miscellaneous">Miscellaneous</a>
# <a id="desc">Description</a>
Tagging a cache is a concept that was invented in the same time by many developers
-([Andrey Smirnoff](http://www.smira.ru), [Dmitryj Koteroff](http://dklab.ru/)
+([Andrey Smirnoff](http://www.smira.ru), [Dmitryj Koteroff](http://dklab.ru/lib/Dklab_Cache/)
and, perhaps, by somebody else)
This software was developed inspired by Andrey Smirnoff's theoretical work
-["Cache tagging with Memcached (on Russian)"](http://www.smira.ru/tag/memcached/).
+[Cache tagging with Memcached (on Russian)](http://www.smira.ru/tag/memcached/).
Some ideas are implemented in the real world (e.i. tag versions based on datetime
and micro time, cache hit/set logging, cache locking) and part of them
are not (atomic counter).
-# <a id="install">Installation</a>
+# <a id="installation">Installation</a>
* As Symfony plugin
@@ -39,7 +40,7 @@ are not (atomic counter).
$ ./symfony cc
$ ./symfony plugin:upgrade sfCacheTaggingPlugin
- * As a git submodule (master or devel branch)
+ * As a git submodule (master branch)
* Installation
@@ -78,7 +79,7 @@ Location: ``/config/ProjectConfiguration.class.php``
## 2. Change default model class
-This will switch default model class ``sfDoctineRecord`` with ``sfCachetaggableDoctrineRecord``
+Switch the default model class ``sfDoctineRecord`` with ``sfCachetaggableDoctrineRecord``
[php]
<?php
@@ -96,7 +97,7 @@ This will switch default model class ``sfDoctineRecord`` with ``sfCachetaggableD
}
}
-Then rebuild your models:
+After, rebuild the models:
$ ./symfony doctrine:build-model
@@ -133,7 +134,7 @@ And don't forget to rebuild models again:
$ ./symfony doctrine:build-model
-## 5. Enable cache and declare required helpers in ``/apps/%APP%/config/settings.yml``:
+## 5. Enable the cache and add mandatory helpers to ``standard_helpers`` (file ``/apps/%APP%/config/settings.yml``):
dev:
.settings:
@@ -298,7 +299,7 @@ And don't forget to rebuild models again:
with_layout: false
enabled: true
-## How to cache ``Doctrine_Record``/``Doctrine_Collection``?
+## How to cache Doctrine query results?
* Does not depends on ``cache.yml`` file
@@ -330,43 +331,64 @@ _NB. Please read "<a href="#quick-setup">Quick setup</a>" before reading this._
## How to cache private blocks (actions/pages/partials) for authenticated users
- Symfony's cache mechanism creates the unique key to each block you want to cache based on
- following arguments:
-
- - Module name
- - Action name
- - $_GET arguments
-
- In case you would like to cache user's private data you must be very careful.
- To prevent users of seeing other user private data you need to add
- additional parameter to distinguish cached blocks among other private blocks.
-
- The easiest way is to keep user_id/username in URL, but it's awful.
- I suggest to add custom $_GET parameter on the fly. This will
- prevent of showing "user_id" in URL.
-
- What should you do is to register a new filter ``AuthParamFilter`` and switch standard
- ``sfWebRequest`` with plugin's one ``sfCacheTaggingWebRequest``.
-
- Place ``AuthParamFilter`` before "caching" filter in ``apps/%application_name%/config/filters.yml``
+ Since version v4.3.0 the classes ``AuthParamFilter`` and ``sfCacheTaggingWebRequest`` are deprecated.
+ This is done because such approach can't handle components and partials (just actions and layouts).
+ So, if you have using ``AuthParamFilter`` (file ``filters.yml``), please disable it.
+ [yaml]
rendering: ~
security: ~
auth_params:
class: AuthParamFilter
+ enabled: false
cache: ~
execution: ~
- Switch to sfCacheTaggingWebRequest in ``apps/%application_name%/config/factories.yml``
+ The new implementation is simple and w/o hacks. It works with actions, components and
+ partials. Here is working example of how to add "user_id" and "user_type" to cache key parameters:
- all:
- request:
- class: sfCacheTaggingWebRequest
+ [php]
+ <?php
+
+ class myUser extends sfBasicSecurityUser
+ {
+ public function initialize (sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
+ {
+ parent::initialize($dispatcher, $storage, $options);
- That's all. Now cache content will be based on additional parameter "user_id" in case
- user have successfully authenticated.
+ $dispatcher->connect('cache.filter_cache_keys', array($this, 'listenOnCacheFilterCacheKeys'));
+ }
+
+ /**
+ * The method is called on condition the user is authenticated.
+ * Also, it's called for each partial/component/action you access on the page.
+ *
+ * Adds 2 custom cache key parameters to any type of cache
+ *
+ * @param $event sfEvent
+ * @param $params array
+ * @return array
+ */
+ public function listenOnCacheFilterCacheKeys (sfEvent $event, array $params)
+ {
+ /* @var $user myUser */
+ $user = $event->getSubject();
+
+ /* @var $viewCache sfViewCacheTagManager */
+ $viewCache = $event['view_cache'];
+
+ /* @var $cacheType int */
+ // Type of the cache sfViewCacheTagManager::NAMESPACE_*
+ $cacheType = $event['cache_type'];
+
+ return array_merge($params, array(
+ 'user_id' => $user->getAttribute('user_id'),
+ 'user_type' => 'BASIC',
+ ));
+ }
+ }
## Explaining ``/config/factories.yml``
@@ -384,11 +406,9 @@ _NB. Please read "<a href="#quick-setup">Quick setup</a>" before reading this._
storage:
class: sfMemcacheTaggingCache
param:
- persistent: true
storeCacheInfo: true
host: localhost
port: 11211
- lifetime: 86400
logger:
class: sfFileCacheTagLogger # to disable logger, set class to "sfNoCacheTagLogger"
@@ -404,15 +424,15 @@ _NB. Please read "<a href="#quick-setup">Quick setup</a>" before reading this._
# There are such available place-holders:
# %char% - Operation char (see char explanation in sfCacheTagLogger::explainChar())
# %char_explanation% - Operation explanation string
- # %time% - Time, when data/tag was accessed
+ # %time% - Time, when data/tag has been accessed
# %key% - Cache name or tag name with its version
- # %microtime% - Micro time timestamp when data/tag was accessed
+ # %microtime% - Micro time timestamp when data/tag has been accessed
# %EOL% - Whether to append \n in the end of line
#
# (Example: "%char% %microtime% %key%%EOL%")
format: "%char%"
-> **Restrictions**: Backend's class should be inherited from ``sfCache``
+> **Restrictions**: Backend's class should be inherited from the ``sfCache``
class. Then, it should be implement ``sfTaggingCacheInterface``
(due to a ``Doctrine`` cache engine compatibility).
Also, it should support the caching of objects and/or arrays.
@@ -458,34 +478,36 @@ Explained behavior setup, file ``/config/doctrine/schema.yml``:
# cache tag will be based on 2 columns
# (e.g. "Article:5:01", "Article:912:00")
# matches the "uniqueColumn" column order
- # (default: "")
+ # (default: "", key format is auto-generated)
uniqueKeyFormat: '%d-%02b'
- # Column name, where object version will be stored in table
+ # Column name, where the object version will be stored in a table
# (default: "object_version")
versionColumn: version_microtime
- # Option to skip object invalidation by changing listed columns
- # Useful for sf_guard_user.last_login or updated_at
+ # Skips the object invalidation if the altered column is in this list
+ # Useful for columns like sf_guard_user.last_login, updated_at
# (default: [])
skipOnChange:
- last_accessed
- # Invalidates or not object collection tag when any
- # record was updated (BC with v2.*)
+ # Invalidates or not the object-collection tag when any
+ # record was just updated (BC with v2.*) associated with this collection-tag.
+ # If the new record is added to collection, or removed - the collection-tag
+ # will be updated in any case.
# Useful, when table contains rarely changed data (e.g. Countries, Currencies)
- # allowed values: true/false
+ # permitted values: true/false
# (default: false)
invalidateCollectionVersionOnUpdate: false
# Useful option when model contains columns like "is_visible", "is_active"
# updates collection tag, if one of columns was updated.
- # will not work if "invalidateCollectionVersionOnUpdate" is set to "true"
- # will not work if one of columns are in "skipOnChange" list.
+ # Would not work if "invalidateCollectionVersionOnUpdate" is set to "true"
+ # Would not work if modified column is in the "skipOnChange" list.
# (default: [])
invalidateCollectionVersionByChangingColumns:
- is_visible
@@ -575,8 +597,11 @@ Explained behavior setup, file ``/config/doctrine/schema.yml``:
# (default: 5)
microtime_precision: 5
- # Callable array
- # Example: [ClassName, StaticClassMethod]
+ # Callable array, or string
+ # Examples:
+ # [ClassName, MethodName]
+ # OR
+ # "ClassName::staticMethodName"
# useful when tag name should contains extra information
# (e.g. Environment name, or application name)
# (default: [])
@@ -623,10 +648,10 @@ Component example:
# adding personal tag
$this->addContentTag('Portal_EN', sfCacheTaggingToolkit::generateVersion());
- # deleting added before tag
+ # remove "Article:31" from content tags
$this->removeContentTag('Article:31');
- # printing all set tags, excepting removed one
+ # print all set tags, excepting the removed one
// var_dump($this->getContentTags());
$this->articles = $articles;
@@ -697,20 +722,13 @@ Set hydration to ``Doctrine_Core::HYDRATE_RECORD`` (NB! using another hydrator,
[php]
<?php
- $q
- ->setHydrationMode(Doctrine_Core::HYDRATE_RECORD)
- ->execute();
-
+ $q->setHydrationMode(Doctrine_Core::HYDRATE_RECORD)->execute();
// or
$q->execute(array(), Doctrine_Core::HYDRATE_RECORD);
Cached ``DQL`` results will be associated with all linked tags based on query results.
-# <a id="misc">Miscellaneous</a>
-
-## New in v4.1.1:
-
- * [Removed] Removing from package test files - all test environment located in GIT repository
+# <a id="miscellaneous">Miscellaneous</a>
## Limitations / Specificity
@@ -723,23 +741,21 @@ Cached ``DQL`` results will be associated with all linked tags based on query re
## TDD
- * Environment: PHP 5.3.8
- * Unit tests: 12
- * Functional tests: 31
- * Checks: 1340
- * Code coverage: 95%
+ * Test environment: PHP 5.4.9, MySQL 5.5.28, Memcached 1.4.10, OS Fedora 17 x64
+ * Number of files: 48
+ * Tests: 1840
+ * Code coverage: 96%
Whether you want to run a plugin tests, you need:
1. Install plugin from GIT repository.
- 2. Install [APC](http://pecl.php.net/package/APC) and [Memcache](http://pecl.php.net/package/Memcache)
+ 2. Install [APC](http://pecl.php.net/package/APC), [Memcache](http://pecl.php.net/package/Memcache) and MySQL
3. Configure ``php.ini`` and restart Apache/php-fpm:
[ini]
[APC]
apc.enabled = 1
apc.enable_cli = 1
- apc.use_request_time = 0
4. Add CLI variable:
@@ -749,7 +765,7 @@ Whether you want to run a plugin tests, you need:
For all further sessions:
- $ echo "export SYMFONY=/path/to/symfony/lib" >> ~/.bashrc
+ $ echo "export SYMFONY=/path/to/symfony/lib" >> ~/.bashrc; source ~/.bashrc
5. Run tests:
@@ -759,6 +775,8 @@ Whether you want to run a plugin tests, you need:
# it will create the ``sfcachetaggingplugin_test`` database
$ ./symfony doctrine:build --all --and-load --env=test
+ $ ./symfony cc
+
# runs unit and functional tests
$ ./symfony test:all
@@ -777,4 +795,4 @@ Whether you want to run a plugin tests, you need:
## Contacts ##
* @: Ilya Sabelnikov `` <fruit dot dev at gmail dot com> ``
- * skype: ilya_roll
+ * Skype: ilya_roll
View
1  VERSION
@@ -0,0 +1 @@
+4.3.0
View
6 config/sfCacheTaggingPluginConfiguration.class.php
@@ -46,13 +46,9 @@ public function initialize ()
$manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
- $this->getEventDispatcher()->notify(
- new sfEvent($manager, 'sf_cache_tagging_plugin.doctrine_configure')
- );
-
$this->getEventDispatcher()->connect(
'component.method_not_found',
- array('sfCacheTaggingToolkit', 'listenOnComponentMethodNotFoundEvent')
+ 'sfCacheTaggingToolkit::listenOnComponentMethodNotFoundEvent'
);
}
}
View
2  lib/cache/extended/sfSQLitePDOTaggingCache.class.php
@@ -87,7 +87,7 @@ public function getMany ($keys)
$rows[$row['key']] = unserialize($row['data']);
}
- # reorder based on passed keys
+ // reorder based on passed keys
$results = array();
foreach ($keys as $key)
{
View
46 lib/cache/sfTaggingCache.class.php
@@ -133,7 +133,7 @@ public function initialize ($options = array())
);
}
- # check is valid class
+ // check is valid class
$this->cache = new $cacheClassName($this->getOption('storage.param', array()));
if (! $this->cache instanceof sfCache)
@@ -160,17 +160,17 @@ public function initialize ($options = array())
);
}
- $this->logger = new $loggerClassName(
- $this->getOption('logger.param', array())
- );
+ $logger = new $loggerClassName($this->getOption('logger.param', array()));
- if (! $this->logger instanceof sfCacheTagLogger)
+ if (! $logger instanceof sfCacheTagLogger)
{
throw new sfInitializationException(sprintf(
'Logger class is not instance of sfCacheTagLogger, got "%s"',
get_class($this->logger)
));
}
+
+ $this->setLogger($logger);
}
/**
@@ -186,12 +186,26 @@ public function getCache ()
/**
* @return sfCacheTagLogger
*/
- protected function getLogger ()
+ public function getLogger ()
{
return $this->logger;
}
/**
+ * Sets a logger
+ *
+ * @since v4.3.0
+ * @param sfCacheTagLogger $logger
+ * @return sfTaggingCache
+ */
+ public function setLogger (sfCacheTagLogger $logger)
+ {
+ $this->logger = $logger;
+
+ return $this;
+ }
+
+ /**
* @since v1.4.0
* parent::has() replaced by $this->get()
* build-in has method does not check if cache
@@ -363,6 +377,8 @@ public function setTag ($key, $tagVersion, $lifetime = null)
*
* @param array $tags
* @param integer $lifetime optional
+ *
+ * @return sfTaggingCache
*/
public function setTags (array $tags, $lifetime = null)
{
@@ -370,6 +386,8 @@ public function setTags (array $tags, $lifetime = null)
{
$this->setTag($tagName, $version, $lifetime);
}
+
+ return $this;
}
/**
@@ -505,26 +523,26 @@ public function get ($key, $default = null)
{
$this->getLogger()->log('V', 'via equal compare');
- # one tag is expired, no reasons to continue
- # (should revalidate cache data)
+ // one tag is expired, no reasons to continue
+ // (should revalidate cache data)
}
else
{
$extendedKeysWithCurrentVersions = array_combine(array_keys($storedTags), array_values($fetchedCacheTags));
- # check for data tags is expired
+ // check for data tags is expired
foreach ($storedTags as $tagKey => $tagLatestVersion)
{
$tagVersion = $extendedKeysWithCurrentVersions[$tagKey];
- # tag is exprired or version is old
+ // tag is exprired or version is old
if (! $tagLatestVersion || $tagVersion < $tagLatestVersion)
{
$this->getLogger()->log(
'v', sprintf('%s(%s=>%s)', $tagKey, $tagVersion, $tagLatestVersion)
);
- # one tag is expired, no reasons to continue
- # (should revalidate cache data)
+ // one tag is expired, no reasons to continue
+ // (should revalidate cache data)
$hasExpired = true;
break;
@@ -541,12 +559,12 @@ public function get ($key, $default = null)
{
if ($this->isLocked($key))
{
- # return old cache coz new data is writing to the current cache
+ // return old cache coz new data is writing to the current cache
$data = $cacheMetadata->getData();
}
else
{
- # cache no locked, but cache is expired
+ // cache no locked, but cache is expired
$data = null;
}
}
View
40 lib/filter/AuthParamFilter.class.php
@@ -15,26 +15,46 @@
*
* @package sfCacheTaggingPlugin
* @subpackage filters
+ * @since v4.2.0
* @author Ilya Sabelnikov <fruit.dev@gmail.com>
+ *
+ * @deprecated since version v4.3.0
*/
class AuthParamFilter extends sfFilter
{
public function execute ($filterChain)
{
- $context = $this->getContext();
+ $message = sprintf(
+ 'The class %s is deprecated since %s v4.3.0. ' .
+ 'Use "cache.filter_cache_keys" event to add custom cache key params.',
+ __CLASS__, sfCacheTaggingToolkit::PLUGIN_NAME
+ );
+
+ $this->getContext()->getEventDispatcher()->notify(
+ new sfEvent($this, 'application.log', array(
+ $message, 'priority' => sfLogger::NOTICE
+ )
+ ));
if ($this->isFirstCall())
{
- if ($context->getUser()->isAuthenticated())
+ $context = $this->getContext();
+
+ /* @var $user sfSecurityUser */
+ $user = $context->getUser();
+ $request = $context->getRequest();
+
+ $callable = array($user, 'getId');
+
+ if (($user instanceof sfSecurityUser)
+ && $user->isAuthenticated()
+ && ($request instanceof sfCacheTaggingWebRequest)
+ && method_exists($user, 'getId') // prevent of triggering __call()
+ && is_callable($callable) // only public method
+ )
{
- $context
- ->getRequest()
- ->addGetParameters(
- array(
- 'user_id' => $context->getUser()->getId(),
- )
- )
- ;
+ /* @var $request sfCacheTaggingWebRequest */
+ $request->addGetParameters(array('user_id' => call_user_func($callable)));
}
}
View
27 lib/log/sfCacheTagLogger.class.php
@@ -115,6 +115,8 @@ public function getOption ($name, $default = null)
* @param string $char One character
* @param string $key Cache name or tag name with version
* (e.g. "CompanyArticle_1(947568127349582")
+ *
+ * @return bool Whether log was written or not
*/
abstract protected function doLog ($char, $key);
@@ -132,7 +134,7 @@ public function log ($char, $key)
return false;
}
- return (boolean) $this->doLog($char, $key);
+ return (bool) $this->doLog($char, $key);
}
/**
@@ -145,7 +147,7 @@ protected function explainChar ($char)
{
switch ($char)
{
- # Data:
+ // Data:
case 'g': return 'data cache not found or expired';
case 'G': return 'data cache was found';
case 'h': return 'cache dot not have data accessed by key';
@@ -159,7 +161,7 @@ protected function explainChar ($char)
case 'r': return 'data cache with no locks';
case 'R': return 'data cache with lock';
- # Tags:
+ // Tags:
case 'v': return 'cache tag version is expired';
case 'V': return 'cache tag version is up-to-date';
case 'p': return 'could not write new version of tag';
@@ -178,6 +180,25 @@ protected function explainChar ($char)
}
/**
+ * Formatts log message
+ *
+ * @param string $char
+ * @param string $key
+ * @return string
+ */
+ protected function getFormattedMessage ($char, $key)
+ {
+ return strtr($this->format, array(
+ '%char%' => $char,
+ '%char_explanation%' => $this->explainChar($char),
+ '%key%' => $key,
+ '%time%' => strftime($this->timeFormat),
+ '%microtime%' => sprintf("%0.0f", pow(10, 5) * microtime(true)),
+ '%EOL%' => PHP_EOL,
+ ));
+ }
+
+ /**
* Executes the shutdown method.
*/
public function shutdown ()
View
30 lib/log/sfFileCacheTagLogger.class.php
@@ -49,14 +49,15 @@ public function initialize (array $options = array())
$dir = dirname($file);
- $umask = umask();
- umask(0000);
+
if (! is_dir($dir))
{
+ $umask = umask(0);
mkdir($dir, $this->getOption('dir_mode'), true);
+ umask($umask);
}
- $fileExists = file_exists($file);
+ $fileExists = is_file($file);
if (! is_writable($dir) || ($fileExists && ! is_writable($file)))
{
@@ -67,26 +68,27 @@ public function initialize (array $options = array())
$this->fp = fopen($file, 'a');
+ if (! $this->fp)
+ {
+ throw new sfFileException(sprintf('Failed to open file "%s" for append', $file));
+ }
+
if (! $fileExists)
{
+ $umask = umask(0);
chmod($file, $this->getOption('file_mode'));
+ umask($umask);
}
-
- umask($umask);
}
+ /**
+ * {@inheritdoc}
+ */
protected function doLog ($char, $key)
{
- if (flock($this->fp, LOCK_EX))
+ if (flock($this->fp, LOCK_EX)) // will wait while lock is present
{
- fwrite($this->fp, strtr($this->format, array(
- '%char%' => $char,
- '%char_explanation%' => $this->explainChar($char),
- '%key%' => $key,
- '%time%' => strftime($this->timeFormat),
- '%microtime%' => sprintf("%0.0f", pow(10, 5) * microtime(true)),
- '%EOL%' => PHP_EOL,
- )));
+ fwrite($this->fp, $this->getFormattedMessage($char, $key));
flock($this->fp, LOCK_UN);
}
View
30 lib/log/sfOutputCacheTagLogger.class.php
@@ -0,0 +1,30 @@
+<?php
+
+ /*
+ * This file is part of the sfCacheTaggingPlugin package.
+ * (c) 2009-2012 Ilya Sabelnikov <fruit.dev@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+ /**
+ * Outputs all log message to the output
+ *
+ * @package sfCacheTaggingPlugin
+ * @subpackage log
+ * @author Ilya Sabelnikov <fruit.dev@gmail.com>
+ */
+ class sfOutputCacheTagLogger extends sfCacheTagLogger
+ {
+ /**
+ * {@inheritdoc}
+ */
+ protected function doLog ($char, $key)
+ {
+ // STDOUT does not work in CLI
+ echo $this->getFormattedMessage($char, $key);
+
+ return true;
+ }
+ }
View
2  lib/request/sfCacheTaggingWebRequest.class.php
@@ -15,6 +15,8 @@
* @package sfCacheTaggingPlugin
* @subpackage request
* @author Ilya Sabelnikov <fruit.dev@gmail.com>
+ *
+ * @deprecated since version v4.3.0
*/
class sfCacheTaggingWebRequest extends sfWebRequest
{
View
52 lib/util/sfCacheTaggingToolkit.class.php
@@ -18,6 +18,7 @@
class sfCacheTaggingToolkit
{
const TEMPLATE_NAME = 'Cachetaggable';
+ const PLUGIN_NAME = 'sfCacheTaggingPlugin';
/**
* @throws sfCacheDisabledException when "sf_cache" is OFF
@@ -35,7 +36,7 @@ public static function getTaggingCache ()
if (! sfContext::hasInstance())
{
throw new sfCacheMissingContextException(
- sprintf('Content is not initialized for "%s"', __CLASS__)
+ sprintf('Context is not initialized for "%s"', __CLASS__)
);
}
@@ -44,8 +45,8 @@ public static function getTaggingCache ()
if (! $viewCacheManager instanceof sfViewCacheTagManager)
{
throw new sfConfigurationException(
- 'sfCacheTaggingPlugin is not properly configured'
- );
+ sprintf('%s is not properly configured', self::PLUGIN_NAME
+ ));
}
return $viewCacheManager->getTaggingCache();
@@ -144,9 +145,9 @@ public static function formatTags ($argument)
$tagsToReturn = $argument->getCacheTags();
}
- # Doctrine_Collection_Cachetaggable and Doctrine_Record are
- # instances of ArrayAccess
- # this check should be after them
+ // Doctrine_Collection_Cachetaggable and Doctrine_Record are
+ // instances of ArrayAccess
+ // this check should be after them
elseif ($argument instanceof ArrayIterator || $argument instanceof ArrayObject)
{
$tagsToReturn = $argument->getArrayCopy();
@@ -182,7 +183,7 @@ public static function formatTags ($argument)
}
/**
- * Listens on "component.method_not_found"
+ * Triggers on "component.method_not_found" event
*
* @param sfEvent $event
* @return null
@@ -230,21 +231,15 @@ public static function getBaseClassName ($className)
{
static $classNames = array();
- $callableArray = sfConfig::get(
- 'app_sfCacheTagging_object_class_tag_name_provider'
- );
-
- if (is_array($callableArray) && (2 == count($callableArray)))
+ if (! array_key_exists($className, $classNames))
{
- if (! array_key_exists($className, $classNames))
- {
- $classNames[$className] = call_user_func($callableArray, $className);
- }
-
- return $classNames[$className];
+ $nameProvider = sfConfig::get('app_sfCacheTagging_object_class_tag_name_provider');
+ $classNames[$className] = is_callable($nameProvider)
+ ? call_user_func($nameProvider, $className)
+ : $className;
}
- return $className;
+ return $classNames[$className];
}
/**
@@ -284,7 +279,7 @@ public static function obtainCollectionVersion ($collectionVersionName)
if (null === $collectionVersion)
{
$collectionVersion = self::generateVersion();
- // Set the generated version in the cache so that subsequent calls to
+ // Set the generated version in the cache so that subsequent calls to
// obtainCollectionVersion return a consistent value
self::getTaggingCache()->setTag($collectionVersionName, $collectionVersion);
}
@@ -292,8 +287,17 @@ public static function obtainCollectionVersion ($collectionVersionName)
return $collectionVersion;
}
-
- public static function obtainTagName (Doctrine_Template_Cachetaggable $template, $objectArray)
+ /**
+ * Creates tag name based on Array hydrated record
+ *
+ * @param Doctrine_Template_Cachetaggable $template
+ * @param array $objectArray
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return string
+ */
+ public static function obtainTagName (Doctrine_Template_Cachetaggable $template, array $objectArray)
{
$uniqueColumns = $template->getOptionUniqueColumns();
@@ -309,8 +313,8 @@ public static function obtainTagName (Doctrine_Template_Cachetaggable $template,
{
throw new InvalidArgumentException(
sprintf(
- 'sfCacheTaggingPlugin: missing values in an array (row from table "%s") - missing key "%s"',
- get_class($table), $columnName
+ '%s: missing values in an array (row from table "%s") - missing key "%s"',
+ self::PLUGIN_NAME, get_class($table), $columnName
)
);
}
View
5 lib/util/sfTagNamespacedParameterHolder.class.php
@@ -81,9 +81,10 @@ public function set ($tagName, $tagVersion, $ns = null)
$this->parameters[$ns] = array();
}
- # skip old tag versions
+ // skip old tag versions
if (
- ! isset($this->parameters[$ns][$tagName]) ||
+ ! isset($this->parameters[$ns][$tagName])
+ ||
$tagVersion > $this->parameters[$ns][$tagName]
)
{
View
27 lib/vendor/Doctrine/Connection/CachetaggableUnitOfWork.php
@@ -50,7 +50,7 @@ public function collectDeletionsAndInvalidations (Doctrine_Record $record)
$this->tagNamesToDelete = array();
$this->tagNamesToInvalidate = array();
- # first record (root element) always goes to collection "tagNamesToDelete"
+ // first record (root element) always goes to collection "tagNamesToDelete"
$this->collect($record, $this->tagNamesToDelete);
}
@@ -89,18 +89,18 @@ private function collect (Doctrine_Record $record, & $definitions)
return;
}
- # delete definitions
+ // delete definitions
if ($this->tagNamesToDelete === $definitions)
{
$definitions[$record->getOid()] = $record->obtainTagName();
$this->cascade($record);
}
- else # invalidate definitions
+ else // invalidate definitions
{
- # do not call cascade - due to SET NULL only updates columns
+ // do not call cascade - due to SET NULL only updates columns
- # do not add tag, if its already on deletion list
+ // do not add tag, if its already on deletion list
if (! array_key_exists($record->getOid(), $this->tagNamesToDelete))
{
$definitions[$record->getOid()] = $record->obtainTagName();
@@ -122,7 +122,7 @@ protected function cascade (Doctrine_Record $record)
{
/* @var $relation Doctrine_Relation_LocalKey */
- # build-in Doctrine cascade mechanism do all the work - skip
+ // build-in Doctrine cascade mechanism do all the work - skip
if ($relation->isCascadeDelete())
{
continue;
@@ -130,7 +130,7 @@ protected function cascade (Doctrine_Record $record)
$cascade = $relation->offsetGet('cascade');
- # no instructions, no results - skip
+ // no instructions, no results - skip
if (0 == count($cascade))
{
continue;
@@ -139,7 +139,7 @@ protected function cascade (Doctrine_Record $record)
$isCascadeDeleteTags = in_array('deleteTags', $cascade);
$isCascadeInvalidateTags = in_array('invalidateTags', $cascade);
- # could be only 1 selected, otherwise skip
+ // could be only 1 selected, otherwise skip
if (! ($isCascadeDeleteTags xor $isCascadeInvalidateTags))
{
continue;
@@ -156,11 +156,7 @@ protected function cascade (Doctrine_Record $record)
$fieldName = $relation->getAlias();
- if (
- $relation->getType() != Doctrine_Relation::ONE
- ||
- isset($record->$fieldName)
- )
+ if ( ! ($relation->getType() == Doctrine_Relation::ONE && isset($record->$fieldName)))
{
$record->refreshRelated($relation->getAlias());
}
@@ -175,7 +171,7 @@ protected function cascade (Doctrine_Record $record)
! isset($definitions[$relatedObjects->getOid()])
)
{
- # invalidate collection version too
+ // invalidate collection version too
$collectionName = sfCacheTaggingToolkit::obtainCollectionName(
$relatedObjects->getTable()
);
@@ -190,6 +186,7 @@ protected function cascade (Doctrine_Record $record)
->getTable()
->getTemplate(sfCacheTaggingToolkit::TEMPLATE_NAME);
+ /* @var $template Doctrine_Template_Cachetaggable */
if ($template->getOption('invalidateCollectionVersionOnUpdate'))
{
$this->tagNamesToInvalidate[$collectionName] = $collectionName;
@@ -212,7 +209,7 @@ protected function cascade (Doctrine_Record $record)
continue;
}
- # invalidate collection version too
+ // invalidate collection version too
$collectionName = sfCacheTaggingToolkit::obtainCollectionName(
$relatedObjects->getTable()
);
View
44 lib/vendor/Doctrine/Template/Cachetaggable.php
@@ -46,16 +46,8 @@ class Doctrine_Template_Cachetaggable extends Doctrine_Template
protected $_state = null;
/**
- * Object unique namespace name to store Doctrine_Record's tags
- *
- * @var string
- */
- protected $invokerNamespace = null;
-
- /**
- * __construct
- *
- * @param string $array
+ * @param array $options
+ * @throws sfConfigurationException
* @return null
*/
public function __construct (array $options = array())
@@ -64,18 +56,15 @@ public function __construct (array $options = array())
$this->getOptions(), $options
);
- $this->invokerNamespace = sprintf(
- '%s/%s', __CLASS__, sfCacheTaggingToolkit::generateVersion()
- );
-
$versionColumn = $this->getOption('versionColumn');
if (! is_string($versionColumn) || 0 >= strlen($versionColumn))
{
throw new sfConfigurationException(
sprintf(
- 'sfCacheTaggingPlugin: "%s" behaviors "versionColumn" ' .
+ '%s: "%s" behaviors "versionColumn" ' .
'should be string and not empty, passed "%s"',
+ sfCacheTaggingToolkit::PLUGIN_NAME,
sfCacheTaggingToolkit::TEMPLATE_NAME,
(string) $versionColumn
)
@@ -147,7 +136,7 @@ public function getCacheTags ($deep = true)
$this->obtainTagName() => $this->obtainObjectVersion(),
);
- $tagHandler->addContentTags($invokerTags, $this->getInvokerNamespace());
+ $tagHandler->addContentTags($invokerTags, $this->obtainInvokerNamespace());
if ($deep)
{
@@ -172,7 +161,7 @@ public function getCacheTags ($deep = true)
$tagHandler->addContentTags(
$reference->getCacheTags($deep),
- $this->getInvokerNamespace()
+ $this->obtainInvokerNamespace()
);
}
}
@@ -181,9 +170,9 @@ public function getCacheTags ($deep = true)
* @todo mistical code (switching added tags with fetch on the fly)
* maybe copy & past from toArray()?
*/
- $tags = $tagHandler->getContentTags($this->getInvokerNamespace());
+ $tags = $tagHandler->getContentTags($this->obtainInvokerNamespace());
- $tagHandler->removeContentTags($this->getInvokerNamespace());
+ $tagHandler->removeContentTags($this->obtainInvokerNamespace());
$this->_state = $stateBeforeLock;
@@ -204,7 +193,7 @@ public function addCacheTags ($tags)
{
$this
->getContentTagHandler()
- ->addContentTags($tags, $this->getInvokerNamespace());
+ ->addContentTags($tags, $this->obtainInvokerNamespace());
return true;
}
@@ -229,7 +218,7 @@ public function addCacheTag ($tagName, $tagVersion)
{
$this
->getContentTagHandler()
- ->setContentTag($tagName, $tagVersion, $this->getInvokerNamespace());
+ ->setContentTag($tagName, $tagVersion, $this->obtainInvokerNamespace());
return true;
}
@@ -332,8 +321,6 @@ public function getOptionKeyFormat ($uniqueColumns)
return $keyFormat;
}
-
-
/**
* Retrieves object unique tag name based on its class
*
@@ -379,7 +366,7 @@ public function obtainObjectVersion ()
/**
* Updates object version
*
- * @return Doctrine_Recotd
+ * @return Doctrine_Record
*/
public function updateObjectVersion ()
{
@@ -387,11 +374,14 @@ public function updateObjectVersion ()
}
/**
- * @return string Object's namespace to store tags
+ * Generates namespace for current invoker
+ *
+ * @since v4.3.0
+ * @return string Object's namespace to store tags
*/
- protected function getInvokerNamespace ()
+ protected function obtainInvokerNamespace ()
{
- return $this->invokerNamespace;
+ return sprintf('%s/%s', __CLASS__, spl_object_hash($this->getInvoker()));
}
/**
View
99 lib/vendor/Doctrine/Template/Listener/Cachetaggable.php
@@ -107,14 +107,14 @@ protected function isModifiedInSkipList ($modified)
*/
protected function isModifiedIsASoftDeleteColumn ($modified, Doctrine_Table $table)
{
- # When SoftDelete behavior saves "deleted" object
- # do not update object version on when "deleted" object is saving
+ // When SoftDelete behavior saves "deleted" object
+ // do not update object version on when "deleted" object is saving
if ($table->hasTemplate('SoftDelete'))
{
$softDeleteTemplate = $table->getTemplate('SoftDelete');
$deleteAtField = $softDeleteTemplate->getOption('name');
- # skip if SoftDelete sets deleted_at field
+ // skip if SoftDelete sets deleted_at field
if (in_array($deleteAtField, $modified))
{
return true;
@@ -162,9 +162,8 @@ public function preDelete (Doctrine_Event $event)
try
{
- $taggingCache = $this->getTaggingCache();
-
$invoker = $event->getInvoker();
+ /* @var $invoker Doctrine_Record */
$unitOfWork = new Doctrine_Connection_CachetaggableUnitOfWork(
$invoker->getTable()->getConnection()
@@ -175,11 +174,6 @@ public function preDelete (Doctrine_Event $event)
$this->preDeleteTagNames = $unitOfWork->getDeletions();
$this->preInvalidateTagNames = $unitOfWork->getInvalidations();
- $taggingCache->setTag(
- $invoker->obtainCollectionName(),
- sfCacheTaggingToolkit::generateVersion()
- );
-
unset($unitOfWork);
}
catch (sfCacheException $e)
@@ -195,19 +189,17 @@ public function preDelete (Doctrine_Event $event)
*/
public function postDelete (Doctrine_Event $event)
{
+ $invoker = $event->getInvoker();
+ /* @var $invoker Doctrine_Record */
+
try
{
$taggingCache = $this->getTaggingCache();
-
- $invoker = $event->getInvoker();
+ $version = sfCacheTaggingToolkit::generateVersion();
$taggingCache->deleteTags($this->preDeleteTagNames);
$taggingCache->invalidateTags($this->preInvalidateTagNames);
-
- $taggingCache->setTag(
- $invoker->obtainCollectionName(),
- sfCacheTaggingToolkit::generateVersion()
- );
+ $taggingCache->setTag($invoker->obtainCollectionName(), $version);
}
catch (sfCacheException $e)
{
@@ -223,11 +215,11 @@ public function postDelete (Doctrine_Event $event)
*/
public function preSave (Doctrine_Event $event)
{
- $this->skipVersionUpdate = true;
-
$invoker = $event->getInvoker();
+ /* @var $invoker Doctrine_Record */
- $this->wasObjectNew = $invoker->isNew();
+ $this->skipVersionUpdate = true;
+ $this->wasObjectNew = ! $invoker->exists();
if (! $invoker->isModified(true))
{
@@ -279,11 +271,12 @@ public function postSave (Doctrine_Event $event)
}
$invoker = $event->getInvoker();
+ /* @var $invoker Doctrine_Record */
$invokerObjectVersion = $invoker->obtainObjectVersion();
$isToInvalidateCollectionVersion
- = (boolean) $this->getOption('invalidateCollectionVersionOnUpdate');
+ = (bool) $this->getOption('invalidateCollectionVersionOnUpdate');
if (! $isToInvalidateCollectionVersion)
{
@@ -301,10 +294,8 @@ public function postSave (Doctrine_Event $event)
*/
if ($isToInvalidateCollectionVersion || ($invoker->exists() && $this->wasObjectNew))
{
- $table = $invoker->getTable();
-
$formatedClassName = sfCacheTaggingToolkit::getBaseClassName(
- $table->getClassnameToReturn()
+ $invoker->getTable()->getClassnameToReturn()
);
$taggingCache->setTag($formatedClassName, $invokerObjectVersion);
@@ -364,6 +355,7 @@ public function preDqlUpdate (Doctrine_Event $event)
}
$table = $event->getInvoker()->getTable();
+ /* @var $table Doctrine_Table */
$collectionVersionName = sfCacheTaggingToolkit::getBaseClassName(
$table->getClassnameToReturn()
@@ -374,7 +366,14 @@ public function preDqlUpdate (Doctrine_Event $event)
*/
if ($this->isModifiedIsASoftDeleteColumn($columnsToModify, $table))
{
- # invalidate collection, if soft delete sets deleted_at field
+ /**
+ * @todo
+ *
+ * Doctrine_Transaction::STATE_SLEEP == $connection->getState()
+ * delay updating tags, and create task, to run on $connection->commit()
+ */
+
+ // invalidate collection, if soft delete sets deleted_at field
$taggingCache->setTag(
$collectionVersionName,
sfCacheTaggingToolkit::generateVersion()
@@ -384,12 +383,13 @@ public function preDqlUpdate (Doctrine_Event $event)
$updateVersion = sfCacheTaggingToolkit::generateVersion();
$q->set($this->getOption('versionColumn'), $updateVersion);
+ // stand-alone query adds SoftDelete filter in both order cases
$selectQuery = $table->createQuery();
$selectQuery->select();
$where = trim(implode(' ', $q->getDqlPart('where')));
- if (! empty ($where))
+ if (! empty($where))
{
$selectQuery->addWhere($where);
}
@@ -412,7 +412,7 @@ public function preDqlUpdate (Doctrine_Event $event)
$taggingCache->setTags($tags);
$isToInvalidateCollectionVersion
- = (boolean) $this->getOption('invalidateCollectionVersionOnUpdate');
+ = (bool) $this->getOption('invalidateCollectionVersionOnUpdate');
if (! $isToInvalidateCollectionVersion)
{
@@ -450,7 +450,10 @@ public function preDqlDelete (Doctrine_Event $event)
return;
}
- $table = $event->getInvoker()->getTable();
+ $invoker = $event->getInvoker();
+ /* @var $invoker Doctrine_Record */
+
+ $table = $invoker->getTable();
/* @var $q Doctrine_Query */
$q = clone $event->getQuery();
@@ -464,19 +467,45 @@ public function preDqlDelete (Doctrine_Event $event)
$this->getOption('versionColumn'),
sfCacheTaggingToolkit::generateVersion()
);
-
- $q->removeDqlQueryPart('set');
}
+ $q->removeDqlQueryPart('set');
+
$params = $q->getParams();
$params['set'] = array();
$q->setParams($params);
+ // when SoftDelete goes after Cachetaggable
+ if ($table->hasTemplate('SoftDelete'))
+ {
+ $softDeleteTemplate = $table->getTemplate('SoftDelete');
+ /* @var $softDeleteTemplate Doctrine_Template_SoftDelete */
+ $eventParams = $event->getParams();
+
+ // If no SoftDelete expression added
+ $alias = "{$eventParams['alias']}.{$softDeleteTemplate->getOption('name')}";
+ if (! $q->contains($alias))
+ {
+ $softDeleteListener = new Doctrine_Template_Listener_SoftDelete(
+ $softDeleteTemplate->getOptions()
+ );
+
+ $softEvent = new Doctrine_Event(
+ $invoker, Doctrine_Event::RECORD_DQL_SELECT, $q, $event->getParams()
+ );
+
+ $softDeleteListener->preDqlSelect($softEvent);
+
+ unset($softDeleteListener, $softEvent);
+ }
+
+ unset($eventParams, $softDeleteTemplate);
+ }
+
$objects = $q->select()->execute();
- $unitOfWork = new Doctrine_Connection_CachetaggableUnitOfWork(
- $q->getConnection()
- );
+ $unitOfWork
+ = new Doctrine_Connection_CachetaggableUnitOfWork($q->getConnection());
foreach ($objects as $object)
{
@@ -489,9 +518,7 @@ public function preDqlDelete (Doctrine_Event $event)
unset($unitOfWork);
$taggingCache->setTag(
- sfCacheTaggingToolkit::getBaseClassName(
- $table->getClassnameToReturn()
- ),
+ sfCacheTaggingToolkit::getBaseClassName($table->getClassnameToReturn()),
sfCacheTaggingToolkit::generateVersion()
);
}
View
301 lib/view/sfViewCacheTagManager.class.php
@@ -30,9 +30,7 @@ class sfViewCacheTagManager extends sfViewCacheManager
* where %s is Page/Action/Partial
*/
const NAMESPACE_PAGE = 'Page';
-
const NAMESPACE_ACTION = 'Action';
-
const NAMESPACE_PARTIAL = 'Partial';
/**
@@ -129,18 +127,14 @@ protected function setEventDispatcher (sfEventDispatcher $eventDispatcher)
*
* @see sfViewCacheManager::initialize()
*/
- public function initialize ($context, sfCache $taggingCache,
- $options = array()
- )
+ public function initialize ($context, sfCache $taggingCache, $options = array())
{
if (! $taggingCache instanceof sfTaggingCache)
{
- throw new InvalidArgumentException(
- sprintf(
- 'Cache "%s" is not instanceof sfTaggingCache',
- get_class($taggingCache)
- )
- );
+ throw new InvalidArgumentException(sprintf(
+ 'Cache "%s" is not instanceof sfTaggingCache',
+ get_class($taggingCache)
+ ));
}
if (! sfConfig::get('sf_cache'))
@@ -158,7 +152,6 @@ public function initialize ($context, sfCache $taggingCache,
$this->request = $context->getRequest();
$this->routing = $context->getRouting();
-
$this->setOptions(array_merge(
array(
'cache_key_use_vary_headers' => true,
@@ -226,7 +219,7 @@ public function get ($internalUri)
*
* Match duplicated as a parent::get()
*
- * Optimezed version, does not call to serialize/unserialize when cache
+ * Optimized version, does not call to serialize/unserialize when cache
* is stored as opcode
*
* @param string $internalUri Internal uniform resource identifier
@@ -245,17 +238,11 @@ protected function _get ($internalUri)
if (sfConfig::get('sf_logging_enabled'))
{
$this->getEventDispatcher()->notify(
- new sfEvent(
- $this,
- 'application.log',
- array(
- sprintf(
- 'Cache for "%s" %s',
- $internalUri,
- $retval !== null ? 'exists' : 'does not exist'
- )
- )
- )
+ new sfEvent($this, 'application.log', array(sprintf(
+ 'Cache for "%s" %s',
+ $internalUri,
+ $retval !== null ? 'exists' : 'does not exist'
+ )))
);
}
@@ -287,11 +274,9 @@ public function set ($data, $internalUri, $tags = array())
if (sfConfig::get('sf_logging_enabled'))
{
$this->getEventDispatcher()->notify(
- new sfEvent(
- $this,
- 'application.log',
- array(sprintf('Save cache for "%s"', $internalUri))
- )
+ new sfEvent($this, 'application.log', array(sprintf(
+ 'Save cache for "%s"', $internalUri
+ )))
);
}
@@ -347,19 +332,15 @@ public function getActionCache ($uri)
if (sfConfig::get('sf_web_debug'))
{
- $content = $this->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $this->getContext()->getResponse(),
- 'uri' => $uri,
- 'new' => false
- )
- ),
- $content
- )
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $this->getContext()->getResponse(),
+ 'uri' => $uri,
+ 'new' => false
+ ));
+
+ $content = $this
+ ->getEventDispatcher()
+ ->filter($event, $content)
->getReturnValue();
}
@@ -384,9 +365,7 @@ public function setActionCache ($uri, $content, $decoratorTemplate)
$contentTags = $this
->getContentTagHandler()
- ->getContentTags(
- self::NAMESPACE_ACTION
- );
+ ->getContentTags(self::NAMESPACE_ACTION);
$actionCacheValue = array(
'content' => $content,
@@ -398,19 +377,15 @@ public function setActionCache ($uri, $content, $decoratorTemplate)
if ($saved && sfConfig::get('sf_web_debug'))
{
- $content = $this->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $this->getContext()->getResponse(),
- 'uri' => $uri,
- 'new' => true
- )
- ),
- $content
- )
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $this->getContext()->getResponse(),
+ 'uri' => $uri,
+ 'new' => true
+ ));
+
+ $content = $this
+ ->getEventDispatcher()
+ ->filter($event, $content)
->getReturnValue();
}
@@ -431,9 +406,7 @@ public function setPageCache ($uri)
$contentTags = $this
->getContentTagHandler()
- ->getContentTags(
- self::NAMESPACE_PAGE
- );
+ ->getContentTags(self::NAMESPACE_PAGE);
$response = $this->getContext()->getResponse();
@@ -442,20 +415,15 @@ public function setPageCache ($uri)
if ($saved && sfConfig::get('sf_web_debug'))
{
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $response,
+ 'uri' => $uri,
+ 'new' => true
+ ));
+
$content = $this
->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $response,
- 'uri' => $uri,
- 'new' => true
- )
- ),
- $response->getContent()
- )
+ ->filter($event, $response->getContent())
->getReturnValue();
$response->setContent($content);
@@ -501,20 +469,15 @@ public function setPartialCache ($module, $action, $cacheKey, $content)
if ($saved && sfConfig::get('sf_web_debug'))
{
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $response,
+ 'uri' => $uri,
+ 'new' => true,
+ ));
+
$content = $this
->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $response,
- 'uri' => $uri,
- 'new' => true,
- )
- ),
- $content
- )
+ ->filter($event, $content)
->getReturnValue();
}
@@ -524,7 +487,8 @@ public function setPartialCache ($module, $action, $cacheKey, $content)
}
/**
- * Listens to the 'view.cache.filter_content' event to decorate a chunk of HTML with cache information.
+ * Listens to the 'view.cache.filter_content' event to decorate a chunk of
+ * HTML with cache information.
*
* Added info about linked tags
*
@@ -595,8 +559,8 @@ public function checkCacheKey (array & $parameters)
if (isset($parameters[$tagsKey]))
{
$tags = true === sfConfig::get('sf_escaping_strategy')
- ? sfOutputEscaper::unescape($parameters[$tagsKey])
- : $parameters[$tagsKey];
+ ? sfOutputEscaper::unescape($parameters[$tagsKey])
+ : $parameters[$tagsKey];
unset($parameters[$tagsKey]);
@@ -630,7 +594,10 @@ public function getPageCache ($uri)
if (sfView::RENDER_VAR == $this->getController()->getRenderMode())
{
- $this->getController()->getActionStack()->getLastEntry()->setPresentation($cachedResponse->getContent());
+ $this->getController()->getActionStack()->getLastEntry()->setPresentation(
+ $cachedResponse->getContent()
+ );
+
$this->getContext()->getResponse()->setContent('');
}
else
@@ -641,20 +608,15 @@ public function getPageCache ($uri)
if (sfConfig::get('sf_web_debug'))
{
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $response,
+ 'uri' => $uri,
+ 'new' => false,
+ ));
+
$content = $this
->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $response,
- 'uri' => $uri,
- 'new' => false,
- )
- ),
- $response->getContent()
- )
+ ->filter($event, $response->getContent())
->getReturnValue();
$response->setContent($content);
@@ -710,20 +672,15 @@ public function getPartialCache ($module, $action, $cacheKey)
if (sfConfig::get('sf_web_debug'))
{
+ $event = new sfEvent($this, 'view.cache.filter_content', array(
+ 'response' => $this->getContext()->getResponse(),
+ 'uri' => $uri,
+ 'new' => false
+ ));
+
$content = $this
->getEventDispatcher()
- ->filter(
- new sfEvent(
- $this,
- 'view.cache.filter_content',
- array(
- 'response' => $this->getContext()->getResponse(),
- 'uri' => $uri,
- 'new' => false
- )
- ),
- $content
- )
+ ->filter($event, $content)
->getReturnValue();
}
@@ -743,7 +700,7 @@ public function disableCache ($moduleName, $actionName = null)
{
if ($moduleName && $actionName)
{
- if (isset($this->cacheConfig[$moduleName], $this->cacheConfig[$moduleName][$actionName]))
+ if (isset($this->cacheConfig[$moduleName][$actionName]))
{
unset($this->cacheConfig[$moduleName][$actionName]);
}
@@ -771,15 +728,15 @@ public function remove ($internalUri, $hostName = '', $vary = '', $contextualPre
if (sfConfig::get('sf_logging_enabled'))
{
$this->getEventDispatcher()->notify(
- new sfEvent(
- $this,
- 'application.log',
- array(sprintf('Remove cache for "%s"', $internalUri))
- )
+ new sfEvent($this, 'application.log', array(sprintf(
+ 'Remove cache for "%s"', $internalUri
+ )))
);
}
- $cacheKey = $this->generateCacheKey($internalUri, $hostName, $vary, $contextualPrefix);
+ $cacheKey = $this->generateCacheKey(
+ $internalUri, $hostName, $vary, $contextualPrefix
+ );
$taggingCache = $this->getTaggingCache();
@@ -792,4 +749,108 @@ public function remove ($internalUri, $hostName = '', $vary = '', $contextualPre
return $taggingCache->remove($cacheKey);
}
}
+
+ /**
+ * Appends extra cache key parameters when user is authenticated
+ * and there are some parameters set by user
+ *
+ * {@inheritdoc}
+ */
+ public function generateCacheKey ($internalUri, $hostName = '', $vary = '', $contextualPrefix = '')
+ {
+ $user = $this->getContext()->getUser();
+
+ $dispatcher = $this->getEventDispatcher();
+
+ // sfSecurityUser interface has "isAuthenticated" method
+ if (! $user instanceof sfSecurityUser)
+ {
+ if (sfConfig::get('sf_logging_enabled'))
+ {
+ $dispatcher->notify(new sfEvent($this, 'application.log', array(
+ sprintf(
+ 'User class "%s" does not implements the "sfSecurityUser" interface',
+ get_class($user)
+ ),
+ 'priority' => sfLogger::DEBUG
+ )));
+ }
+ }
+ elseif (! $user->isAuthenticated())
+ {
+ if (sfConfig::get('sf_logging_enabled'))
+ {
+ $dispatcher->notify(new sfEvent($this, 'application.log', array(
+ 'User is not authenticated', 'priority' => sfLogger::DEBUG
+ )));
+ }
+ }
+ else
+ {
+ $urlInfo = parse_url($internalUri);
+ $sysParams = array();
+
+ if (isset($urlInfo['query']))
+ {
+ parse_str($urlInfo['query'], $sysParams);
+ }
+
+ $cacheType = null;
+
+ if (0 === strpos($internalUri, '@sf_cache_partial'))
+ {
+ $cacheType = self::NAMESPACE_PARTIAL;
+ }
+ elseif (! isset($sysParams['action'], $sysParams['module']))
+ {
+ // w/ layout is only pages (action+layout)
+ if ($this->withLayout($internalUri))
+ {
+ $cacheType = self::NAMESPACE_PAGE;
+ }
+ else // w/o layout only actions
+ {
+ $cacheType = self::NAMESPACE_ACTION;
+ }
+ }
+
+ $event = new sfEvent($user, 'cache.filter_cache_keys', array(
+ 'view_cache' => $this,
+ 'cache_type' => $cacheType,
+ 'call_args' => func_get_args(),
+ ));
+
+ $userParams = $dispatcher->filter($event, array())->getReturnValue();
+
+ if (! is_array($userParams))
+ {
+ $dispatcher->notify(new sfEvent($this, 'application.log', array(
+ 'Custom cache key param is not an array', 'priority' => sfLogger::ERR
+ )));
+ }
+ elseif (0 < count($userParams))
+ {
+ // protects @sf_cache_partial action/module name substitution
+ if (isset($userParams['action'])) unset($userParams['action']);
+ if (isset($userParams['module'])) unset($userParams['module']);
+
+ $formattedUserParams = array();
+ foreach ($userParams as $key => $value)
+ {
+ $key = is_numeric($key) ? sprintf('param_%d', $key) : $key;
+ $formattedUserParams[$key] = $value;
+ }
+
+ // union, do not overwrite sysParams key values
+ $sysParams += $formattedUserParams;
+
+ if (0 < count($sysParams))
+ {
+ $internalUri = $urlInfo['path'] . '?' . http_build_query($sysParams);
+ }
+ }
+ }
+
+ return parent::generateCacheKey($internalUri, $hostName, $vary, $contextualPrefix);
+ }
}
View
139 package.xml
@@ -8,10 +8,11 @@
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>sfCacheTaggingPlugin</name>
<channel>pear.symfony-project.com</channel>
- <summary>Smart caching plugin.</summary>
+ <summary>Smart caching plugin</summary>
<description>
- The ``sfCacheTaggingPlugin`` is a Symfony plugin, that helps to keep cached
- content up-to-date without setting cache lifetime.
+ The ``sfCacheTaggingPlugin`` is a Symfony plugin that allows you to not think about
+ cache obsolescence. The user will see only a fresh data thanks to cache tagging.
+ The plugin helps to keep cached content up-to-date without setting cache lifetime.
So-called &quot;Smart caching&quot;, stores cache with fetched models tags and its versions.
Each time you save/update/delete objects, plugin increments object tag version and
all linked cache by specific tag name will automatically spoil.
@@ -22,11 +23,11 @@
<email>fruit.dev@gmail.com</email>
<active>yes</active>
</lead>
- <date>2012-02-05</date>
- <time>12:30:00</time>
+ <date>2012-12-16</date>
+ <time>01:46:00</time>
<version>
- <release>4.2.2</release>
- <api>4.0.1</api>
+ <release>4.3.0</release>
+ <api>4.0.2</api>
</version>
<stability>
<release>stable</release>
@@ -34,85 +35,88 @@
</stability>
<license uri="http://www.symfony-project.com/license">MIT license</license>
<notes>
- * ilya: [fixed] Minor fix to obtainCollectionVersion (thanks paulkmoore)
- * ilya: [updated] Renewed copyright years
+ * [fixed] When ``SoftDelete`` installed after ``Cachetaggable`` (``actAs`` section), ``preDqlDelete`` forces to update already softly deleted records (thanks to Paul Moore)
+ * [added] New approach to append custom parameter(-s) to the cache key only for authenticated users
+ * [added] more coverage tests
</notes>
<contents>
<dir name="/">
+ <file name="VERSION" md5sum="c9e4dc50b8d6ed20385fdca95873b3a5" role="data" />
+ <file name="README" md5sum="0ef0726af6520d1a8525550638c3ee8a" role="data" />
<dir name="config">
+ <file name="sfCacheTaggingPluginConfiguration.class.php" md5sum="9176da04833be7fe68d5a2b3fb28be9f" role="data" />
<file name="app.yml" md5sum="6f7e6faa779ba005cad6c48da40797fe" role="data" />
- <file name="sfCacheTaggingPluginConfiguration.class.php" md5sum="7b3a2d4c0e26176c02ec322692fb52aa" role="data" />
</dir>
<file name="LICENSE" md5sum="405a172e07fbccb9037268666541a1c0" role="data" />
- <file name="README" md5sum="83e627222228fe70a4a2592d013b04ad" role="data" />
<dir name="lib">
- <dir name="util">
- <file name="sfCacheTaggingToolkit.class.php" md5sum="8b5efb6695eae08c32d3b5a89409bafa" role="data" />
- <file name="sfContentTagHandler.class.php" md5sum="c33ef8b775ef8c76e2b042897db357f0" role="data" />
- <file name="sfCallableArray.class.php" md5sum="1bd34a890342ec1122aec9810f9efbb0" role="data" />
- <file name="sfTagNamespacedParameterHolder.class.php" md5sum="9bb2fe10eff473c7dd04dbd750ad28fc" role="data" />
- <file name="sfViewCacheTagManagerBridge.class.php" md5sum="80d91e2f2f1a65f8fc21398eb2220aa6" role="data" />
+ <dir name="vendor">
+ <dir name="Doctrine">
+ <dir name="Connection">
+ <file name="CachetaggableUnitOfWork.php" md5sum="fc38510767f5c8cac279eb0fd83b22e7" role="data" />
+ </dir>
+ <dir name="Cache">
+ <file name="Proxy.php" md5sum="00d2fba131ea03f283f56bd41aba2c2f" role="data" />
+ </dir>
+ <dir name="Collection">
+ <file name="Cachetaggable.php" md5sum="b0924ba0e6c679696012e17daf05a9d2" role="data" />
+ </dir>
+ <dir name="Template">
+ <file name="Cachetaggable.php" md5sum="dad9f36a3ebc7e297c398f77e571162e" role="data" />
+ <dir name="Listener">
+ <file name="Cachetaggable.php" md5sum="e0d9dbafea40f00ea6b615e6a384a97a" role="data" />
+ </dir>
+ </dir>
+ <dir name="Query">