After implementing query caching using Doctrine’s Doctrine_Cache_Apc interface in a Symfony application I am working on, when running the application’s functional tests, warnings were returned intermittently in a few places
$ [apc-warning] Potential cache slam averted for key ...
In APC >= 3.0, an apc.slam_defense configuration option was added in attempt to avoid repeated writes to the same APC cache key as might occur under very high traffic. This apc.slam_defense option was later removed due to having been deprecated in favor of apc.write_lock as of APC >= 3.0.11. This is enabled by default, so the first thing I tried in my development environment was disabling this option. A simple test case will still fail though with apc.write_lock disabled.
apc_store('my_key', 1);
apc_store('my_key', 2);
echo apc_fetch('my_key'); // outputs 1, not 2
One of the more recent comments in this PECL ticket for APC offers a patch that can be applied to the latest release of APC before compiling it. This patch re-introduces the previously removed apc.slam_defense option. For my development environment only (since the cache slam warnings thrown in this environment due to my functional tests are irrelevant), I fixed the cache slam warnings by following these steps
php.ini, add the newly recognized apc.slam_defense=0 With apc.slam_defense in place and disabled, the test case above and my application’s functional tests run without any cache slam warnings.
Doctrine provides a stock Sluggable template behavior that can be applied to models, generating a slug based on one or more columns for a Doctrine_Record instance.
In most cases it is best practice not to modify a slug after it is generated, since one or more external sources may be referencing a URL to the Doctrine_Record instance by the original slug. If the slug is modified, those URLs referencing the original slug will become broken. For this reason, Doctrine’s default behavior is to have the canUpdate option for the Sluggable template be set to false.
There may however be reason to override this disabled canUpdate behavior under certain conditions. Doctrine 1.2 does not seem to provide an easy way to access a specific Doctrine_Record_Listener and modify an option associated with that listener for a given Doctrine_Record instance. Assuming there is only a single instance of any given listener associated with a Doctrine_Record, it’s fairly straightforward to find the desired listener within the Doctrine_Record_Listener_Chain. Applying this search conditionally to a Doctrine_Record::preUpdate() hook would look something like is shown below.
// In your Doctrine_Record class
public function preUpdate($event){
if (true) { // Conditions to check against for whether or not to allow the slug to be modified go here
try {
SomeUtilityClass::getListener($event->getInvoker()->getListener(), 'Sluggable')->setOption('canUpdate', true);
} catch (Exception $e) {
// Silently fail
}
}
}
// In SomeUtilityClass
class SomeUtilityClass {
static public function getListener(Doctrine_Record_Listener_Chain $chain, $search) {
$i = 0;
do {
if ($chain[$i] == null) {
throw new InvalidArgumentException('The requested Doctrine_Record_Listener is not associated with this Doctrine_Record instance');
}
if (get_class($chain[$i]) === sprintf('Doctrine_Template_Listener_%s', $search)) {
return $chain[$i];
}
} while (++$i);
}
}
The above getListener() method is required since it appears the only access to Doctrine_Record_Listener is via an implementation of PHP’s predefined ArrayAccess interface. Since the Doctrine_Record_Listener_Chain class only implements the set() and get() methods from the ArrayAccess interface, the only way to access the listeners is via their automatically assigned numeric keys. There is no way to directly request a specific listener by name through the Doctrine 1.2 API, thus the necessity for the do...while loop.
To prevent user-generated content formatted with Markdown from being re-parsed by the Markdown parser every time it is called to be rendered as HTML, it is best practice to store the parsed/converted HTML version of the original Markdown text alongside the original Markdown markup. The parsing of this user-generated content can only be done as necessary using Doctrine’s preSave event listener.
First, the schema for the table can be very simple.
Message:
columns:
message:
type: text
notnull: true
raw_message:
type: text
notnull: true
The raw_message field will contain the Markdown text as entered by the user. The message field will contain the converted HTML equivalent of the user-generated Markdown syntax.
When saving the record with Doctrine, only the raw_message field needs to be set manually.
$message = new Message();
$message->setRawMessage($some_posted_form_message);
$message->save();
Finally, in the Message class (an implementation of Doctrine_Record), a preSave() event listener is added to conditionally parse the user-generated Markdown into HTML and store it to the message field.
class Message extends BaseMessage {
public function preSave($event){
if (!$this->exists() || array_key_exists('raw_message', $this->getModified())) {
$markdown = new MarkdownExtra_Parser();
// strip all HTML tags
$value = htmlentities($this->raw_message, ENT_QUOTES, 'UTF-8');
$this->message = $markdown->transform($value);
}
}
// ...
}
Now, whenever a Message is saved, if the raw_message field value has been changed, it will be re-parsed, converted to HTML and stored into the message field.
Hoping there is a simpler way that I am missing, here is how to begin a new transaction with Doctrine 1.2 through Symfony 1.4. It was the call to sfDoctrineDatabase::getDoctrineConnection() which I had to lookup in the docs1.
$connection = sfContext::getInstance()->getDatabaseManager()->getDatabase('default')->getDoctrineConnection();
$connection->beginTransaction();
try {
// Do work for the transaction ...
$connection->commit();
} catch (Exception $e) {
$connection->rollback();
// Handle exception ...
}
Some Conversation model might have a many-to-many relationship between itself and some Participant model through some ConversationsParticipants model. By default in Doctrine, to get all Participants for some Conversation, the magic finder for
$participants = Conversation->Participants;
would return Participant instances in the order they were created (ordered by the primary key). When a custom ordering of the list of Participants is required, the magic finder must be overloaded within the base model. For example, if the participants are to be ordered by username ASC, we would create a getParticipants() method in the Conversation model like this
public function getParticipants(){
$q = DoctrineQuery::create()
->from('User u')
->innerJoin('u.ConversationsParticipants cu')
->where('cu.conversation_id = ?', $this->id)
->orderBy('u.username ASC');
return $q->execute();
}
This is great for models which have been persisted in the database already, but becomes problematic when retrieving the Participants relation for some new Conversation which has yet to be saved into the database. Since the above overloaded getParticipants() finder is querying the database against a Conversation which does not yet exist in the database, we need to conditionally ignore this custom finder’s query and default back to the magic finder within the Doctrine_Record class.
public function getParticipants(){
if ($this->isNew()) {
$q = DoctrineQuery::create()
->from('User u')
->innerJoin('u.ConversationsParticipants cu')
->where('cu.conversation_id = ?', $this->id)
->orderBy('u.username ASC');
return $q->execute();
} else {
return parent::__get('Participants');
}
}