<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://unfoldingneurons.com/"
	>

<channel>
	<title>Larry Ullman&#039;s Blog &#187; framework</title>
	<atom:link href="http://blog.dmcinsights.com/tag/framework/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.dmcinsights.com</link>
	<description>flotsam and jetsam abounds</description>
	<lastBuildDate>Tue, 27 Jul 2010 16:27:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=abc</generator>
		<item>
		<title>Handling Checkboxes in Yii with non-Boolean Values</title>
		<link>http://blog.dmcinsights.com/2010/07/25/handling-checkboxes-in-yii-with-non-boolean-values/</link>
		<comments>http://blog.dmcinsights.com/2010/07/25/handling-checkboxes-in-yii-with-non-boolean-values/#comments</comments>
		<pubDate>Sun, 25 Jul 2010 15:24:08 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=1205</guid>
		<description><![CDATA[On a recent Yii-based project, managing one of the Models required a whole slew of checkboxes to indicate that yes, the quality does apply, or no, it does not. In this case, the value being stored in the database for each attribute was a single letter: Y/N. However Yii, when showing the form to update [...]]]></description>
			<content:encoded><![CDATA[<p>On a recent Yii-based project, managing one of the Models required a whole slew of checkboxes to indicate that yes, the quality does apply, or no, it does not.  In this case, the value being stored in the database for each attribute was a single letter: Y/N. However Yii, when showing the form to update an item, needs the checkbox value to be a Boolean, in order to properly pre-check the box. Changing the database wasn&#8217;t an option in this case, so I had to figure out a good conversion process. In this post, I&#8217;ll tell you exactly how I solved this issue.<span id="more-1205"></span>As I said, for the item in question, the database table had multiple columns, each of which only stored either <em>Y</em> or <em>N</em>. As an example, think of a users table with multiple opt-in options: newsletters, offers, etc. When the user creates and updates their account, presenting these as checkboxes is appropriate. But for the administrator, it&#8217;d probably be best to view user preferences by just seeing Y/N for each option (although the code I&#8217;m going to present wouldn&#8217;t be significantly different if you stored 1/0).</p>
<p>The <strong>protected/views/modelName/_form.php</strong> file defines the form that&#8217;s used to both create and update an individual record. In it, checkboxes are created using:</p>
<pre>&lt;?php echo $form-&gt;checkBox($model,'<em>attribute</em>'); ?&gt;</pre>
<p>I can pass an array of options to the <strong>checkbox()</strong> method in order to tweak how it behaves. In my example, I want to set the values assigned to the Model in both the checked and unchecked states. Here&#8217;s how you do that:</p>
<pre>&lt;?php echo $form-&gt;checkBox($model,'<em>attribute</em>',array('value' =&gt; 'Y', 'uncheckValue'=&gt;'N')); ?&gt;</pre>
<p>The <em>value</em> option literally creates the <strong>value=&#8221;Y&#8221;</strong> code in the checkbox HTML. The <em>uncheckValue</em> option is something that Yii adds that allows you to indicate what the value should be if the box is not checked. It&#8217;s a nice addition, saving you from having to add code in the Model to define the value when the box isn&#8217;t checked.</p>
<p>So now my Model will receive, and the database will store, either <em>Y</em> or <em>N</em>, depending upon whether the box is checked or not. The next hurdle comes for properly handling updates. An update uses this same form, but the above code alone will not automatically check the box should the database store the value <em>Y</em> for the given attribute. My solution, then, was to convert <em>Y</em> to true and <em>N</em> to false when retrieving the Model item, but only on an update. In the other situations where a Model record might be retrieved and used, the Y/N values are preferable.</p>
<p>In the Controller for this Model, the update action is where I want to start my changes. That method first loads an individual Model using the Controller&#8217;s <strong>loadModel()</strong> method:</p>
<pre>public function actionUpdate() {
    $model=$this-&gt;loadModel();</pre>
<p>After this point, I want to convert all instances of Y/N to true/false, so I invoke a Model method (I&#8217;ll define next) to perform that task:</p>
<pre>$model-&gt;convertToBooleans();</pre>
<p>The <strong>convertToBooleans()</strong> method is defined in the Model. It first defines an array of all attributes that need the conversion:</p>
<pre>public function convertToBooleans() {
    $attributes = array('newsletter', 'offers', ...);</pre>
<p>This list of strings must exactly match the names of the corresponding Model attributes. From here, there are a couple of ways to proceed. One obvious route is to loop through the array using a <strong>foreach</strong>. Within the <strong>foreach</strong>, the ternary operator is used to assign to the Model&#8217;s attribute a Boolean based upon its current value:</p>
<pre>foreach ($attributes as $attr) {
    $this-&gt;$attr = ($this-&gt;$attr == 'Y') ? true : false;
}</pre>
<p>So that&#8217;s all you need to do to make this work. But if you&#8217;re like me and you prefer to make your methods as atomic as possible, you could create a separate Model method that performs the specific conversion:</p>
<pre>public function convertAttributeToBoolean($attr) {
    $this-&gt;$attr = ($this-&gt;$attr == 'Y') ? true : false;
}</pre>
<p>In this case, the <strong>convertToBooleans()</strong> method needs to call this other method for each item in the array. The <strong>array_map()</strong> PHP function can apply a function to every element in an array:</p>
<pre>array_map('someFunction', $someArray);</pre>
<p>To use an object method, use this syntax:</p>
<pre>array_map(array($object, 'methodName'), $someArray);</pre>
<p>So the <strong>convertToBooleans()</strong> method could do this:</p>
<pre>array_map(array($this, 'convertAttributeToBoolean'), $attributes);</pre>
<p>And that&#8217;s what you need to do to make form checkboxes work with non-Boolean database values (MySQL does support storing of Booleans, though). Remember that you first need to update the <strong>_form.php</strong> script, which will be used for both adding and updating records. Then you only need to convert the Y/N values to Booleans when you go to update a record.</p>
<p>I hope this helps and thanks for reading. Let me know if you have any questions or comments. Thanks, Larry!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/07/25/handling-checkboxes-in-yii-with-non-boolean-values/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Forcing Login for All Pages in Yii</title>
		<link>http://blog.dmcinsights.com/2010/07/20/forcing-login-for-all-pages-in-yii/</link>
		<comments>http://blog.dmcinsights.com/2010/07/20/forcing-login-for-all-pages-in-yii/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 06:45:17 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[auth]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=1195</guid>
		<description><![CDATA[Some time back, I had written a couple of blog posts on authentication and authorization in Yii. As a comment to one of those posts, someone shared some code (also posted in the Yii forums) that requires a login to access any page. The interesting thing about this code is that it&#8217;s placed in the [...]]]></description>
			<content:encoded><![CDATA[<p>Some time back, I had written a couple of blog posts on <a href="http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/">authentication</a> and <a href="http://blog.dmcinsights.com/2010/01/14/yii-framework-access-control-lists/">authorization</a> in <a href="http://www.yiiframework.com/">Yii</a>. As a comment to one of those posts, someone shared some code (also posted in the Yii forums) that requires a login to access any page. The interesting thing about this code is that it&#8217;s placed in the primary application configuration file, not within individual Controllers. The benefit to this approach is that a little bit of code can add authorization to your entire site, no matter how many Controllers you have. I&#8217;ll explain how to use this approach in this post, although keep in mind that it&#8217;s really best for situations where users must be logged in to access almost all of the site&#8217;s content.<span id="more-1195"></span>The very first step uses the <span style="text-decoration: underline;">protected/config/main.php</span> configuration file. That configuration file returns an array of values. Within that primary array (i.e., not within any of the subsections), you&#8217;ll want to add:</p>
<pre>'behaviors' =&gt; array(
    'onBeginRequest' =&gt; array(
        'class' =&gt; 'application.components.RequireLogin'
    )
),
</pre>
<p>By placing this within the primary array, it applies to the application as a whole, as opposed to a specific component or module. This code associates one class with the <strong>onBeginRequest</strong> behavior. This is to say that every time a request is made, an instance of the <strong>RequireLogin</strong> class should be created.</p>
<p>Next, create a file called <span style="text-decoration: underline;">RequireLogin.php</span> and store it in the <span style="text-decoration: underline;">protected/components</span> directory. That file needs to define the <strong>RequireLogin</strong> class, which should be an extension of the Yii <strong>CBehavior</strong> class, which defines how application behaviors are used:</p>
<pre>&lt;?php
class RequireLogin extends CBehavior
{
}
?&gt;</pre>
<p>Within that class, only two methods need to be defined. The first is <strong>attach()</strong>, which will associate an event handler with the application:</p>
<pre>public function attach($owner)
{
    $owner-&gt;attachEventHandler('onBeginRequest', array($this, 'handleBeginRequest'));
}</pre>
<p>This method will receive the application as an argument (this code was found in the Yii forums, too). The <strong>attachEventHandler()</strong> function attaches to the application an event handler, saying that when the <strong>onBeginRequest</strong> event occurs, this class&#8217;s <strong>handleBeginRequest()</strong> method should be called.</p>
<p>The <strong>handleBeginRequest()</strong> method is defined like so:</p>
<pre>public function handleBeginRequest($event)
{
    if (Yii::app()-&gt;user-&gt;isGuest &amp;&amp; !in_array($_GET['r'],array('site/login'))) {
        Yii::app()-&gt;user-&gt;loginRequired();
    }
}</pre>
<p>The method takes one argument, which will be the event (not actually used in the method). The purpose of the method is to determine the conditions in which a login must be required of the user. That enforcement is made by calling <strong>Yii::app()-&gt;user-&gt;loginRequired()</strong>. For this bare-bones example, the condition checks if the user is a guest, which is to say they aren&#8217;t logged in, and that <strong>$_GET['r'] </strong>does not have a value of <em>site/login</em>. The net effect is that guests can only ever access the login page. If you wanted to allow access to other pages, just add those values to the array:</p>
<pre>if (Yii::app()-&gt;user-&gt;isGuest &amp;&amp; !in_array($_GET['r'],array('site/login', 'site/index', 'site/contact'))) {
</pre>
<p>So that&#8217;s one way of implementing a broad login requirement without individually adjusting each Controller. To be clear, you&#8217;ll probably need to do that some as well, like to allow for access to specific actions based upon the type of logged-in user. Still, this is a simple and quite effective catchall. The person that shared the original code in my blog had put all this together within the configuration file. It is possible to do that (by creating an executed function) but the syntax is tricky and the code can really muddle up your configuration file. I think it&#8217;s best to separate it out, plus you now have a new class (<strong>RequireLogin</strong>) that you can use in other Yii-based sites.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/07/20/forcing-login-for-all-pages-in-yii/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Lerdorf&#8217;s No-Framework PHP MVC Framework</title>
		<link>http://blog.dmcinsights.com/2010/07/17/lerdorfs-no-framework-php-mvc-framework/</link>
		<comments>http://blog.dmcinsights.com/2010/07/17/lerdorfs-no-framework-php-mvc-framework/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 16:47:07 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=1166</guid>
		<description><![CDATA[Some years ago, Rasmus Lerdorf, original creator of PHP, posted on his personal Web site a discussion of what he would look for in a framework for the MVC architecture. It&#8217;s an interesting read, from the master&#8217;s voice. He specifically talks about how to properly use MVC, how to guarantee performance and security, and how [...]]]></description>
			<content:encoded><![CDATA[<p>Some years ago, Rasmus Lerdorf, original creator of <a href="http://www.php.net">PHP</a>, posted on <a href="http://toys.lerdorf.com/">his personal Web site</a> a<a href="http://toys.lerdorf.com/archives/38-The-no-framework-PHP-MVC-framework.html"> discussion of what he would look for in a framework for the MVC architecture</a>. It&#8217;s an interesting read, from the master&#8217;s voice. He specifically talks about how to properly use MVC, how to guarantee performance and security, and how to design with scalability and flexibility in mind. Although the post is a few years old, most of it is still valid, although there are little changes such as the fact that the PECL/Filter extension is now part of PHP proper.</p>
<p>Lerdorf also posted <a href="http://toys.lerdorf.com/archives/53-HipHop-PHP-Nifty-Trick.html">his own thoughts</a> on <a href="http://developers.facebook.com/blog/post/358">Facebook&#8217;s use of HipHop PHP</a> for faster PHP execution, if you&#8217;re looking for something to read.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/07/17/lerdorfs-no-framework-php-mvc-framework/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Yii Framework&#8217;s New Gii Tool</title>
		<link>http://blog.dmcinsights.com/2010/05/18/yii-frameworks-new-gii-tool/</link>
		<comments>http://blog.dmcinsights.com/2010/05/18/yii-frameworks-new-gii-tool/#comments</comments>
		<pubDate>Tue, 18 May 2010 19:08:14 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=1062</guid>
		<description><![CDATA[Version 1.1.2 of the Yii framework came out a couple of weeks ago (May 2, 2010) and it&#8217;s main new feature is Gii. Gii is a Web-based alternative to the yiic command-line tool. Both yiic and Gii are used to generate code in existing Yii Web applications. For example, you create the database for a [...]]]></description>
			<content:encoded><![CDATA[<p>Version 1.1.2 of the <a href="http://www.yiiframework.com">Yii framework</a> came out a couple of weeks ago (May 2, 2010) and it&#8217;s main new feature is <a href="http://www.yiiframework.com/doc/guide/topics.gii">Gii</a>. Gii is a Web-based alternative to the yiic command-line tool. Both yiic and Gii are used to generate code in existing Yii Web applications. For example, you create the database for a project, then use the command-line yiic tool to generate the shell of the project (its directory, configuration files, subdirectories, etc.), and then you would turn to Gii in the Web browser to create Model, Controller, and View files, along with the standard CRUD functionality. I&#8217;ll write more about Gii soon, but in the meantime, check out the previous <a href="http://www.yiiframework.com/doc/guide/topics.gii">Gii</a> link or <a href="http://www.yiiframework.com/doc/guide/quickstart.first-app">this part of the quickstart documentation</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/05/18/yii-frameworks-new-gii-tool/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Yii Framework Access Control Lists</title>
		<link>http://blog.dmcinsights.com/2010/01/14/yii-framework-access-control-lists/</link>
		<comments>http://blog.dmcinsights.com/2010/01/14/yii-framework-access-control-lists/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 14:51:51 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[access control list]]></category>
		<category><![CDATA[acl]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=478</guid>
		<description><![CDATA[In my series Learning the Yii Framework, I discuss the individual parts of the MVC (Model, View, Controller) architecture in some detail, from a Yii perspective. In the post on , I introduce Access Control Lists (ACLs), Yii&#8217;s default way of restricting who can take what actions. This is a key part of the security [...]]]></description>
			<content:encoded><![CDATA[<p>In my series <a href="http://blog.dmcinsights.com/series/learning-the-yii-framework/">Learning the Yii Framework</a>, I discuss the individual parts of the MVC (Model, View, Controller) architecture in some detail, from a Yii perspective. In the post on <a href="http://blog.dmcinsights.com/2009/11/15/basic-controller-edits-in-yii/">Controllers</a>, I introduce <em>Access Control Lists</em> (ACLs), Yii&#8217;s default way of restricting who can take what actions. This is a key part of the security of any Web application. For example, a site&#8217;s content can often be read by anyone at all, registered or non-registered users alike (like the text you&#8217;re reading now). Some content may only be viewable by registered users and some by registered users of a certain type (e.g., paid members). Finally, some content may only be viewable by administrators. In this post, I detail how to completely control access to your Web application using Yii&#8217;s Access Control Lists.<span id="more-478"></span>To start by repeating what I wrote in my <a href="http://blog.dmcinsights.com/2009/11/15/basic-controller-edits-in-yii/">Basic Controller Edits</a> post, a Controller&#8217;s <strong>accessRules()</strong> method dictates who can do what. In a very simple way, the &#8220;what&#8221; refers to the Controller&#8217;s action methods, like <strong>actionList()</strong> or <strong>actionDelete()</strong>. In other words, only X type of user can call the <strong>actionDelete()</strong> method, which, of course, deletes a record. Your &#8220;who&#8221; depends upon the situation, but to start there&#8217;s at least logged-in and not logged-in users, represented by <strong>*</strong> (anyone) and <strong>@</strong> (logged-in users), accordingly. Depending upon the login system in place, you may also have levels of users. So the <strong>accessRules()</strong> method uses all this information and returns an array of values. The values are also arrays, indicating permissions (<em>allow</em> or <em>deny</em>), actions, and users:</p>
<pre>public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'list' and 'show' actions
            'actions'=&gt;array('list','show'),
            'users'=&gt;array('*'),
        ),
        array('allow', // allow authenticated user to perform 'create' and 'update' actions
            'actions'=&gt;array('create','update'),
            'users'=&gt;array('@'),
        ),
        array('allow', // allow admin user to perform 'admin' and 'delete' actions
            'actions'=&gt;array('admin','delete'),
            'users'=&gt;array('admin'),
        ),
        array('deny',  // deny all users
            'users'=&gt;array('*'),
        ),
    );
}</pre>
<p>That&#8217;s the default setting for a Controller, where anyone can perform list and show actions, meaning that anyone can list all records or show individual records in an associated Model. The next section allows any logged-in user to perform create and update actions. Next, only administrators can perform admin and delete actions. Finally, a global deny for all users is added, to cover any situation that wasn&#8217;t explicitly defined. This is just a good security practice. Note that these rules just apply to this Controller; each Controller needs its own rules.</p>
<p>So at the most basic level, your access rules begin by allowing or denying actions to logged-in or non-logged-in users. When you go to create your own rules, start with what anyone can do and end with what no one can do. For example, say you have some user management system that includes the Models <strong>User</strong> and <strong>UserType</strong>, corresponding to related tables in the database. The <strong>UserType</strong> would be a simple two-field object—<em>id</em> and<em> type</em>, where type might be <em>reader</em>, <em>writer</em>, <em>editor</em>, and so forth. Each <strong>User</strong> then is assigned a single, specific type. I&#8217;d be inclined to grant list and show permissions to everyone, as that might be useful for browsing users by type and would be necessary for adding new users. However, I probably wouldn&#8217;t allow create, update, admin (which is really a variation on list), or delete permissions on <strong>UserType</strong> at all, with the thinking that these are static values. The <strong>accessRules()</strong> would then be:</p>
<pre>public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'list' and 'show' actions
            'actions'=&gt;array('list','show'),
            'users'=&gt;array('*'),
        ),
        array('deny', // no one can create, update, or delete these:
            'actions'=&gt;array('create','update','admin','delete'),
            'users'=&gt;array('*'),
        ),
        array('deny',  // deny all users anything not specified
            'users'=&gt;array('*'),
        ),
    );
}</pre>
<p>As you can see, just to be extra careful, I include a final deny clause still: if actions are added later the default behavior will be denial; only by then changing the rules can that action be executed by anyone.</p>
<p>To take this beyond logged-in vs. non-logged-in users, you need to know that the representation of logged-in users relies upon Yii&#8217;s authentication components. In the default rules, the admin user can perform certain actions, where &#8220;admin&#8221; is the actual name of the user that is logged in, and comes from <strong>protected/components/UserIdentity.php</strong>.  I write about the authentication process in detail in two posts: the <a href="http://blog.dmcinsights.com/2010/01/04/simple-authentication-with-the-yii-framework/">first covering simple authentication</a>, <a href="http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/">the second covering more evolved authentication</a>. The default Yii application allows for two users, with names of <em>demo</em> and <em>admin</em>, but you&#8217;d likely have a more elaborate system in place. If you know only a limited number of users will be admins, you could hardcode their usernames into the rules:</p>
<pre>array('allow', // allow harold and maude user to perform 'admin' and 'delete' actions
    'actions'=&gt;array('admin','delete'),
    'users'=&gt;array('harold','maude'),
),
</pre>
<p>This is still a bit too static, though. Perhaps your users in the database would be registered by user type, or <em>role</em>, and that the permissions would be based upon these roles. In that case, you can add an expression element to the returned array in the access rules:</p>
<pre>array('allow',
    'actions'=&gt;array('admin','delete'),
    'users'=&gt;array('@'),
    'expression'=&gt;'PHP code to be evaluated'
),
</pre>
<p>Two things about this expression. First, you still need to use the <em>users</em> element, and you&#8217;ll probably want to still restrict this to logged-in users (most likely). Second, the expression itself should be some PHP code, <span style="text-decoration: underline;">quoted</span>, that when evaluated gives a Boolean result. If the code in the expression will be true, then permission will be allowed; false, denied. Say you wanted to restrict the publish action to only those with the role of editor:</p>
<pre>array('allow',
    'actions'=&gt;array('publish'),
    'users'=&gt;array('@'),
    'expression'=&gt;'isset($user-&gt;role) &amp;&amp; ($user-&gt;role==="editor")'
),
</pre>
<p>The <strong>$user-&gt;role</strong> value would have to be established when the user logs in, as part of the authentication process. I discuss this exact example in my <a href="http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/">second post that covers more evolved authentication</a>. And I put the entire expression within quotes (it doesn&#8217;t matter whether you use single or double, so long as you don&#8217;t create a parse error). With that rule, users that aren&#8217;t logged in, or users that are logged in but have non-editor roles, won&#8217;t be able to take that action.</p>
<p>A final way in which I&#8217;ve enforced authorization in previous Yii projects involves a bit of a hack. I had a site where users with a certain role could create certain types of content. They could also update or delete those types of content, but only if they were the one to create that content in the first place. In other words, say a user creates an event, then their user ID gets associated with that event&#8217;s record. The update and delete actions in the <strong>EventController</strong> should only be executable if the currently-logged-in user&#8217;s ID is the same as the <strong>ownerId</strong> of the event in question. As far as I know, this cannot be addressed in the <strong>accessRules()</strong> method, because those rules are evaluated prior to the loading of any specific Model. My solution, perhaps a hack but it works, was to add the proper logic within the actions. Here&#8217;s a simplified version of how that would look within one of the actions:</p>
<pre>public function actionDelete()
{
    $event = $this-&gt;loadEvents(); // Fetch the specific event Model.
    // Can only delete the events they created:
    if ($event-&gt;ownerId == Yii::app()-&gt;user-&gt;id) {
        $event-&gt;delete();
    } else {
        throw new CHttpException(403,'Invalid request. You are not allowed to delete this event.');
    }
}</pre>
<p>As I said, that&#8217;s a hack but it works fine for me and is simple enough to follow (also, the actions that display events for editing and deleting get changed to only list those with a matching <strong>ownerId</strong>). A better solution would likely be to use <a href="http://www.yiiframework.com/doc/guide/topics.auth#role-based-access-control">Yii&#8217;s Role-Based Access Control (RBAC)</a>. I haven&#8217;t personally gone down that path yet as I haven&#8217;t had the need; the above knowledge has more than sufficed for the sites I&#8217;ve created thus far.</p>
<p>As always, thanks for reading and let me know what comments or questions you may have. Thanks, Larry</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/01/14/yii-framework-access-control-lists/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Custom Authentication using the Yii Framework</title>
		<link>http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/</link>
		<comments>http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/#comments</comments>
		<pubDate>Thu, 07 Jan 2010 06:15:18 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[auth]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=849</guid>
		<description><![CDATA[In a , I walk through the Yii framework&#8217;s built-in authentication system for adding login functionality to a Web site. There are a number of files and components involved, but simple authentication works fine out of the box, or with just a little tweaking. That&#8217;s the focus of that previous post. In this post, I [...]]]></description>
			<content:encoded><![CDATA[<p>In a <a href="http://blog.dmcinsights.com/2010/01/04/simple-authentication-with-the-yii-framework/">previous post</a>, I walk through the <a href="http://www.yiiframework.com">Yii</a> framework&#8217;s built-in authentication system for adding login functionality to a Web site. There are a number of files and components involved, but simple authentication works fine out of the box, or with just a little tweaking. That&#8217;s the focus of that previous post. In this post, I explain how you can customize the authentication process further.<span id="more-849"></span>The default authentication behavior allows users to login with hardcoded username/password combinations. In this post, I want to change that behavior so that:</p>
<ul>
<li>Authentication is performed against a database table</li>
<li>The user&#8217;s email address is used instead of their username</li>
<li>The user&#8217;s ID is stored for later reference</li>
<li>The user&#8217;s &#8220;role&#8221; is stored for later reference</li>
</ul>
<p>To start, let&#8217;s assume there is a database table called <em>User</em> that&#8217;s already been modeled and populated. The table has several columns, including at least: <em>id</em>, <em>email</em>, <em>password</em>, and <em>role</em>. The password column stores a <strong>SHA1()</strong>-encrypted version of the user&#8217;s password. The role dictates what the user can do in the site. Possible values are <em>reader</em>, <em>editor</em>, and <em>writer </em>(these are hypothetical values; they could be anything).</p>
<p>Now, by default, Yii will use cookies for authentication. In most situations that&#8217;s fine, but if anything of a sensitive nature is being stored, you should use sessions instead. This would apply to both the user&#8217;s ID value and their role. If either is available through a cookie, it wouldn&#8217;t be hard for the user to edit that cookie&#8217;s value in order to become someone else. So, to start, let&#8217;s disable the potential for using cookies. To do that, open up the <strong>protected/config/main.php</strong> configuration file and find this section from under components:</p>
<pre>'user'=&gt;array(
    // enable cookie-based authentication
    'allowAutoLogin'=&gt;true,
),</pre>
<p>To disable cookie-based authentication, either remove that code entirely, or change <em>allowAutoLogin</em> to false.</p>
<p>Next, let&#8217;s turn to the login form, which will require a couple of alterations. Open up <strong>protected/views/site/login.php</strong>, which is the form. The default form looks like this:</p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_1.png"><img class="aligncenter size-full wp-image-836" title="Yii Login Form" src="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_1.png" alt="Yii Login Form" width="582" height="195" /></a></p>
<p>First, we need to remove the hint paragraph, as demo/demo and admin/admin will no longer work. Then we remove the code that displays the &#8220;remember me&#8221; checkbox. Remember me functionality is only good for cookies, so it&#8217;s useless here. Finally, we want to take an email address, not a username, so those two lines must be changed. The complete form file is now:</p>
<pre>&lt;?php $this-&gt;pageTitle=Yii::app()-&gt;name . ' - Login'; ?&gt;

&lt;h1&gt;Login&lt;/h1&gt;

&lt;div&gt;
&lt;?php echo CHtml::beginForm(); ?&gt;

&lt;?php echo CHtml::errorSummary($form); ?&gt;

&lt;div&gt;
&lt;?php echo CHtml::activeLabel($form,'email'); ?&gt;
&lt;?php echo CHtml::activeTextField($form,'email') ?&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;?php echo CHtml::activeLabel($form,'password'); ?&gt;
&lt;?php echo CHtml::activePasswordField($form,'password') ?&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;?php echo CHtml::submitButton('Login'); ?&gt;
&lt;/div&gt;

&lt;?php echo CHtml::endForm(); ?&gt;

&lt;/div&gt;&lt;!-- yiiForm --&gt;</pre>
<p>Next, we turn to the <strong>LoginForm</strong> Model, defined in <strong>protected/models/LoginForm.php</strong>. This Model is associated with the login form, handling and validating that submitted data. At the top of the Model, the class variables are defined. We need to change <strong>$username</strong> to <strong>$email</strong> and remove <strong>$rememberMe</strong>:</p>
<pre>class LoginForm extends CFormModel
{
    public $email;
    public $password;</pre>
<p>Next, alter the rules accordingly. Instead of username and password being required, email and password are required. Also, the email value should be in a valid email address format. The application of the <strong>authenticate()</strong> method to validate the password remains. Here are the updated rules:</p>
<pre>public function rules()
{
    return array(
        array('email, password', 'required'),
        array('email', 'email'),
        array('password', 'authenticate'),
    );
}</pre>
<p>Next, if you want, change the <strong>attributeLabels()</strong> method for the email address and remove the label for rememberMe:</p>
<pre>public function attributeLabels()
{
    return array('email'=&gt;'Email Address');
}</pre>
<p>The final changes to the <strong>LoginForm</strong> Model are in the <strong>authenticate()</strong> method. Several references to username must be changed to email. For example, this:</p>
<pre>$identity=new UserIdentity($this-&gt;username,$this-&gt;password);</pre>
<p>becomes:</p>
<pre>$identity=new UserIdentity($this-&gt;email,$this-&gt;password);</pre>
<p>Then there&#8217;s a call to <strong>UserIdentity::authenticate()</strong>, which is where the actual authentication against the database takes place (see my previous post and I&#8217;ll also return to this shortly). After that, there&#8217;s an important switch conditional that responds to three possibilities: authenticated, invalid username, and invalid password. The applicable case is signaled by a <strong>UserIdentity</strong> constant. Do note that the <strong>UserIdentity</strong> class is an extension of <strong>CUserIdentity</strong>, so it uses <strong>ERROR_USERNAME_INVALID</strong> instead of <strong>ERROR_EMAIL_INVALID</strong>. In the following code I treat username and email address as synonymous, because it&#8217;s the easiest solution. A full alteration would require changing the Yii framework&#8217;s definition of <strong>CBaseUserIdentity</strong> (which <strong>CUserIdentity</strong> extends), which is not a good idea. So here&#8217;s the modified switch:</p>
<pre>switch($identity-&gt;errorCode)
{
    case UserIdentity::ERROR_NONE:
        Yii::app()-&gt;user-&gt;login($identity);
        break;
    case UserIdentity::ERROR_USERNAME_INVALID:
        $this-&gt;addError('email','Email address is incorrect.');
        break;
    default: // UserIdentity::ERROR_PASSWORD_INVALID
        $this-&gt;addError('password','Password is incorrect.');
        break;
}</pre>
<p>In the first case, with no error present, the user is logged in. I&#8217;ve removed references to <em>rememberMe</em> and <em>duration</em>, both of which are present in the Yii-generated code. The second case applies if the email address was not found in the database. Again, the error code is <strong>ERROR_USERNAME_INVALID</strong>, but it applies just the same. We do want to change the error so that it applies to the email element and has the proper error message. The final case applies if the email address was found but the supplied password was incorrect. Here&#8217;s an image for how the login form looks and behaves after these modifications:</p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_5.png"><img class="aligncenter size-full wp-image-863" title="Modified Login Form with Errors" src="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_5.png" alt="Modified Login Form with Errors" width="581" height="255" /></a></p>
<p>Finally, we turn to <strong>protected/components/UserIdentity.php</strong>, for the final changes. There are a couple of things we need to do in this file. First, we need to perform the authentication against the <strong>User</strong> Model (and therefore, the database). Second, we need to store the user&#8217;s ID and role in the session for later use in the site. For the authentication, we&#8217;ll modify the <strong>authenticate()</strong> method:</p>
<pre>public function authenticate()
{
    $user = User::model()-&gt;findByAttributes(array('email'=&gt;$this-&gt;username));</pre>
<p>Now the <strong>$user</strong> object represents the <strong>User</strong> record with an email field equal to the submitted email address. You may be wondering why I refer to <strong>$this-&gt;username</strong> here. That&#8217;s because the <strong>CUserIdentity</strong> class&#8217;s constructor takes the provided email address and password (from <strong>LoginForm</strong>) and stores them in <strong>$this-&gt;username</strong> and <strong>$this-&gt;password</strong>. So I need to equate username with email here, which is better than editing the framework itself. You ought to leave a comment about this so that you won&#8217;t be confused later when looking at the code.</p>
<p>Next the <strong>authenticate()</strong> method checks a series of possiblities and assigns constant values to the <strong>errorCode</strong> variable:</p>
<pre>if ($user===null) { // No user found!
    $this-&gt;errorCode=self::ERROR_USERNAME_INVALID;
} else if ($user-&gt;password !== SHA1($this-&gt;password) ) { // Invalid password!
    $this-&gt;errorCode=self::ERROR_PASSWORD_INVALID;</pre>
<p>In the first conditional, if <strong>$user</strong> has no value, then no records were found, so the email address was incorrect. In the second conditional, the stored password is compared against the <strong>SHA1()</strong> version of the submitted password. This assumes the record&#8217;s password was stored in a <strong>SHA1()</strong>-encrypted format. If neither of these conditionals are true, then everything is okay:</p>
<pre>} else { // Okay!
    $this-&gt;errorCode=self::ERROR_NONE;
    // Store the role in a session:
    $this-&gt;setState('role', $user-&gt;role);
}</pre>
<p>As you can see, a constant representing no error is assigned to the error code. After that, the user&#8217;s role value, from the database table, is stored in the session. This is accomplished by invoking the <strong>setState()</strong> method. Provide it with a name, <em>role</em>, and a value. After you&#8217;ve done this, the user&#8217;s role will be available through <strong>Yii::app()-&gt;user-&gt;role</strong>. You could do the same thing to store the user&#8217;s ID in the session, but the built-in authentication already has a <strong>getId()</strong> method that returns the user&#8217;s identifier. By default, the method returns the username value, so you&#8217;ll need to override the default behavior to return the ID instead. Start by creating a private variable in <strong>UserIdentity</strong>:</p>
<pre>class UserIdentity extends CUserIdentity
{
    // Need to store the user's ID:
    private $_id;</pre>
<p>Then, in the else clause (for successful authentication), assign the user&#8217;s ID to the class ID variable:</p>
<pre>$this-&gt;_id = $user-&gt;id;</pre>
<p>Finally, after the <strong>authenticate()</strong> method, override the <strong>getId()</strong> method by redefining it as:</p>
<pre>public function getId()
{
    return $this-&gt;_id;
}
</pre>
<p>Now the user&#8217;s ID will be available through <strong>Yii::app()-&gt;user-&gt;id</strong>.</p>
<p>And that should be it. You now have an authentication process based upon an email address and password combination, using a database table, that also stores two pieces of information in the session. As always, thanks for reading and let me know if you have any questions or comments. Thanks, Larry!</p>
<p><strong>Edit: Per a request, here&#8217;s the database schema, the LoginForm.php Model file, and the components/UserIdentity.php script that I *think* I used for this post:</strong></p>
<pre>CREATE TABLE `User` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `email` varchar(80) NOT NULL,
 `pass` char(40) NOT NULL,
 `role` enum('reader','editor','writer') NOT NULL,
 PRIMARY KEY (`id`)
);
</pre>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/LoginForm.php_.zip">LoginForm.php</a></p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/UserIdentity.php_.zip">UserIdentity.php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/feed/</wfw:commentRss>
		<slash:comments>46</slash:comments>
		</item>
		<item>
		<title>Simple Authentication with the Yii Framework</title>
		<link>http://blog.dmcinsights.com/2010/01/04/simple-authentication-with-the-yii-framework/</link>
		<comments>http://blog.dmcinsights.com/2010/01/04/simple-authentication-with-the-yii-framework/#comments</comments>
		<pubDate>Mon, 04 Jan 2010 20:36:48 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[auth]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=826</guid>
		<description><![CDATA[I wanted to write up a strong post on Access Control Lists in Yii, for controlling who can do what in an application. I still will, of course, but since authorization depends upon authentication, I thought it necessary to discuss Yii&#8217;s authentication system first. And, as happens with me, in writing about that, and how [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to write up a strong post on Access Control Lists in <a href="http://www.yiiframework.com">Yii</a>, for controlling who can do what in an application. I still will, of course, but since authorization depends upon authentication, I thought it necessary to discuss Yii&#8217;s authentication system first. And, as happens with me, in writing about that, and how you would <a href="http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/">customize the authentication process</a>, I had to break the subject down into two posts. So here&#8217;s the first of an informal three-part series on authentication and authorization in Yii. In this post, I discuss how the parts of Yii&#8217;s authentication system work together; much of what I say in this first post is also available in the Yii <a href="http://www.yiiframework.com/doc/">documentation</a>, just not presented in this way. I also show a couple of quick ways to modify its behavior to suit your situation.<span id="more-826"></span></p>
<p>Most sites use some form of authentication. Maybe users have to login to post questions, to read content, or to administer the site. Whatever the case, a verification of the person is required before they can do whatever. As this is standard behavior, the default application created by the Yii framework has built-in authentication. When you generate a new site using Yii&#8217;s command-line tools, three files for managing authentication are created:</p>
<ul>
<li><strong>protected/components/UserIdentity.php</strong></li>
<li><strong>protected/models/LoginForm.php</strong></li>
<li><strong>protected/views/site/login.php</strong></li>
</ul>
<p>And there&#8217;s also some code added to <strong>protected/controllers/SiteController.php</strong> that comes into play. The Controller file, gets the action going, of course. The View file is the login form itself. The <strong>LoginForm</strong> Model file defines the rules and behavior and the <strong>UserIdentity</strong> file defines a Model that performs the actual authentication.</p>
<p>By default, if you do nothing at all, users will be allowed to log into the site using the username/password combinations of <em>demo</em>/<em>demo</em> or <em>admin</em>/<em>admin</em>. These values are set in the <strong>UserIdentity.php</strong> script. If all you need to do is allow only a single authenticated user, then you can open <strong>UserIdentity.php</strong> and change this code:</p>
<pre>$users=array(
    // username =&gt; password
    'demo'=&gt;'demo',
    'admin'=&gt;'admin',
);</pre>
<p>to</p>
<pre>$users=array(
    'whateverName'=&gt;'whateverPassword'
);</pre>
<p>At that point, you&#8217;re done. However, you most likely will perform authentication against a database table, so let&#8217;s see how you&#8217;d edit these files to make that happen. To start, let&#8217;s assume that the table name is <em>Users</em>, that you&#8217;ve already generated a Model for it, and that authentication requires an username and a password. As you&#8217;ll see, you&#8217;ll still only need to edit the <strong>UserIdentity.php</strong> file to make this possible, but in my next post I&#8217;ll walk through more elaborate customizations. Okay, with that in mind, let&#8217;s examine the authentication process&#8230;</p>
<p>The URL to login will likely be <span style="text-decoration: underline;">www.example.com/index.php/site/login</span>, as Yii puts login/logout functionality in the Site Controller by default. So when the user clicks on a link to go to the login page, they&#8217;ll go through the Site Controller and call the <strong>actionLogin()</strong> method. That method is defined as:</p>
<pre>public function actionLogin()
{
    $form=new LoginForm;
    // collect user input data
    if(isset($_POST['LoginForm']))
    {
        $form-&gt;attributes=$_POST['LoginForm'];
        // validate user input and redirect to previous page if valid
        if($form-&gt;validate()) $this-&gt;redirect(Yii::app()-&gt;user-&gt;returnUrl);
    }
    // display the login form
    $this-&gt;render('login',array('form'=&gt;$form));
}</pre>
<p>First, a new object is created of type <em>LoginForm</em>. That class is defined in the <strong>LoginForm.php</strong> Model file. If the form has been submitted, the form data is collected and the data is validated. If the data passes the validation, the user will be redirected to whatever URL got them here in the first place. If the form has not been submitted, or if the form data does not pass the validation routine, then the login form is displayed, and it is passed the <strong>LoginForm</strong> object. The default login form looks like this (note the reference to demo/demo and admin/admin, already discussed):</p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_1.png"><img class="aligncenter size-full wp-image-836" title="Yii Login Form" src="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_1.png" alt="Yii Login Form" width="582" height="195" /></a></p>
<p>Now, the call to the <strong>validate()</strong> method in the above code means that the form data has to pass the validation rules established in the <strong>LoginForm</strong> class. This is basic Model validation as defined by the <strong>rules()</strong> method of the Model (also see <a href="http://blog.dmcinsights.com/2009/11/10/basic-model-edits-in-yii/">my post on basic Model edits</a>):</p>
<pre>public function rules()
{
    return array(
        // username and password are required
        array('username, password', 'required'),
        // password needs to be authenticated
        array('password', 'authenticate'),
    );
}</pre>
<p>As you can see, those rules say that both the username and password are required and that the password has to pass the <strong>authenticate()</strong> method. This is not a built-in validation routine, but is defined in <strong>LoginForm</strong>. The <strong>LoginForm::</strong><strong>authenticate()</strong> method starts off like so:</p>
<pre>public function authenticate($attribute,$params)
{
    if(!$this-&gt;hasErrors())  // we only want to authenticate when no input errors
    {
        $identity=new UserIdentity($this-&gt;username,$this-&gt;password);
        $identity-&gt;authenticate();
        switch($identity-&gt;errorCode)</pre>
<p>If there are no errors when this method is called (an error would be a lack of a username or password), a <strong>UserIdentity</strong> object is created, passing that object the submitted username and password. Then the <strong>UserIdentity</strong> object&#8217;s <strong>authenticate()</strong> method is called. This gets us to <strong>protected/components/UserIdentity.php</strong>, which defines the class and the method. If you&#8217;re having trouble following the process thus far, here it is pictorially:</p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_2.png"><img class="aligncenter size-full wp-image-840" title="Yii Auth Process" src="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_2.png" alt="Yii Auth Process" width="451" height="361" /></a></p>
<p>Note that any errors stops the process from continuing and just re-displays the login form, with the errors reported.</p>
<p>As I already wrote above, the <strong>UserIdentity</strong> <strong>authenticate()</strong> method as written compares the submitted values against predefined combinations and returns a message accordingly. If you want to test the submitted values against the database, you&#8217;ll need to add some code. Start by fetching the record for the given username:</p>
<pre>class UserIdentity extends CUserIdentity
{
    public function authenticate()
    {
        $user = User::model()-&gt;findByAttributes(array('username'=&gt;$this-&gt;username));
</pre>
<p>Now the <strong>$user</strong> object represents the <strong>User</strong> record with an username field equal to the submitted username. (The <strong>CUserIdentity</strong> class&#8217;s constructor takes the provided username and password and stores them in <strong>$this-&gt;username</strong> and <strong>$this-&gt;password</strong>, just to be clear.) You could, and might be inclined, to attempt to retrieve the record that matches both the username AND the password, but if you were to do that, you wouldn&#8217;t be able to provide more meaningful error messages: username doesn&#8217;t exist, username does exist but the password doesn&#8217;t match, and so forth.</p>
<p>Next the <strong>authenticate()</strong> method should check a series of possiblities and assigns constant values to the <strong>errorCode</strong> variable:</p>
<pre>if ($user===null) { // No user found!
    $this-&gt;errorCode=self::ERROR_USERNAME_INVALID;
} else if ($user-&gt;pass !== SHA1($this-&gt;password) ) { // Invalid password!
    $this-&gt;errorCode=self::ERROR_PASSWORD_INVALID;</pre>
<p>In the first conditional, if <strong>$user</strong> has no value, then no records were found, so the username was incorrect. In the second conditional, the stored password is compared against the <strong>SHA1()</strong> version of the submitted password. This assumes the record&#8217;s password was stored in a <strong>SHA1()</strong> encrypted format. If neither of these conditionals are true, then everything is okay:</p>
<pre>} else { // Okay!
    $this-&gt;errorCode=self::ERROR_NONE;
}</pre>
<p>Finally, the method returns a boolean value, indicating an error or not:</p>
<pre>return !$this-&gt;errorCode;</pre>
<p>The presence of an error code gets used in the <strong>authenticate()</strong> method of the <strong>LoginForm</strong> Model, in response to the authentication attempt (this is a continuation of the <strong>LoginForm::authenticate()</strong> code shown above):</p>
<pre>$identity-&gt;authenticate();
switch($identity-&gt;errorCode)
{
    case UserIdentity::ERROR_NONE:
        $duration=$this-&gt;rememberMe ? 3600*24*30 : 0; // 30 days
        Yii::app()-&gt;user-&gt;login($identity,$duration);
        break;
    case UserIdentity::ERROR_USERNAME_INVALID:
        $this-&gt;addError('username','Username is incorrect.');
        break;
    default: // UserIdentity::ERROR_PASSWORD_INVALID
        $this-&gt;addError('password','Password is incorrect.');
        break;
}</pre>
<p>So if there was no error, the user is logged in. The <em>duration</em>—the period for which they&#8217;ll be logged in—is either 30 days or for just the session, depending upon whether they checked the <strong>rememberMe</strong> box or not (see the login form image). If either error is present, it&#8217;ll be added to the current object, the validation will fail, meaning that the conditional in the <strong>actionLogin()</strong> method of <strong>SiteController.php</strong> will be false, and the login form will be rendered again, this time with the error message. The whole process therefore becomes:</p>
<p><a href="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_3.png"><img class="aligncenter size-full wp-image-841" title="Yii Auth Process, Complete" src="http://blog.dmcinsights.com/wp-content/uploads/2010/01/yii_auth_3.png" alt="Yii Auth Process, Complete" width="447" height="354" /></a></p>
<p>That&#8217;s an outline of the basic process, using a <strong>User</strong> Model (based upon a database table), instead of hard-coded values. All of this information can also be found in the Yii documentation, just not written quite like this. To take this information further, and to make it more practical, my next post will show you how to modify all of the Yii-generated code in order to:</p>
<ul>
<li>Store the user&#8217;s ID so it can be referenced as they peruse the site</li>
<li>Store the user&#8217;s &#8220;role&#8221;, like reader, editor, and writer, as well.</li>
<li>Authenticate the user with their email address and password, not their username.</li>
</ul>
<p>That post will follow in just a couple of days. EDIT: <a href="http://blog.dmcinsights.com/2010/01/07/custom-authentication-using-the-yii-framework/">Here it is</a>! As always, thanks for your interest in what I have to say and let me know if you have any questions or comments. Thanks, Larry</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2010/01/04/simple-authentication-with-the-yii-framework/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Integrating Zend_Lucene with Yii</title>
		<link>http://blog.dmcinsights.com/2009/12/05/integrating-zend_lucene-with-yii/</link>
		<comments>http://blog.dmcinsights.com/2009/12/05/integrating-zend_lucene-with-yii/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 06:47:16 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[yii]]></category>
		<category><![CDATA[zend]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=790</guid>
		<description><![CDATA[I&#8217;m just not a big fan of using the Zend Framework as my Web development tool, but one of the framework&#8217;s nicest features is that you can use only the parts of it you need. I am, however, a big fan of the Yii framework and one of its many plusses is that you can [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m just not a big fan of using the Zend Framework as my Web development tool, but one of the framework&#8217;s nicest features is that you can use only the parts of it you need. I am, however, a big fan of the Yii framework and one of its many plusses is that you can easily integrate other frameworks and tools into it. Like, for example, the Zend Framework. Yii does not have its own search engine functionality, and Apache&#8217;s Lucene is arguably the gold standard (although clearly not the only choice), so tapping into Zend&#8217;s Lucene module for a Yii-driven site makes a lot of sense. In this post, I&#8217;ll walk you through the steps for integrating  Zend_Lucene into Yii. This post does assume familiarity with PHP, MVC, and Yii.<span id="more-790"></span>To start, let&#8217;s create a spot in the Yii application for the Zend Framework. Create a new directory called <strong>vendors</strong> within the Yii <strong>protected</strong> folder. This isn&#8217;t required, but as the Zend Framework is a different beast than all the Yii code, I think it&#8217;s best to separate it out. Within <strong>vendors</strong>, create a directory called <strong>Zend</strong> (or ZendFramework, if you&#8217;d rather).</p>
<p>Next, <a href="http://framework.zend.com/download/latest">download the Zend Framework</a>. You&#8217;ll want to download the latest full package, even though you&#8217;ll only use a bit of it. After the download has completed, expand the ZIP or TAR.GZ file (whichever format you choose to download the framework in). The result will be a folder named <strong>ZendFramework-<em>x.y.z</em></strong>. (where <em>x.y.z</em> represent the full version number). Within that folder, go into <strong>library/Zend</strong> and copy <strong>Exception.php</strong> to <strong>protected/vendors/Zend</strong>. This is the file that the Zend Framework uses to report problems, so you&#8217;ll want to include it while developing and debugging Zend_Lucene with Yii. Also copy the <strong>Search</strong> folder to <strong>protected/vendors/Zend</strong>. You&#8217;ll end up with a structure like this:</p>
<p>In terms of the MVC architecture, the Zend Framework provides the Model to be used by this search process, but the Controller and View will still be done using Yii. First, let&#8217;s write a new Controller for searching:</p>
<pre>class SearchController extends CController
{
    private $_indexFiles = '../runtime/search';
    public function actionIndex() {}
    public function actionCreate() {}
    public function actionSearch() {}
    public function actionUpdate() {}
}</pre>
<p>As with all Yii Controllers, this one extends the base <span style="text-decoration: underline;">CController</span> class. Within this Controller the various methods are defined, corresponding to the actions that&#8217;ll be taken in the search process. The <em>index</em> action is the default and is for accessing the search page without performing an actual search (e.g., clicking on a link to go to the search page). The <em>create</em> action will be used to generate the search database: the series of files that Lucene needs to perform its searches. The <em>search</em> action is for handling submission of the search form (i.e., it does the actual searching). Finally, the <em>update</em> action is for updating the Lucene database files when necessary (like when the site content changes). The class also has one private variable that stores the location on the server of Lucene database files. I chose to put them in a <strong>search</strong> folder found within <strong>runtime</strong> (<strong>protected/runtime/search</strong>). This class member is good to have as multiple methods will need this information but I create it as a private variable as it&#8217;s not necessary (nor should it be accessed) outside of the class. As a naming convention, some like to use underscores at the front of private class variables.</p>
<p>Within three of the methods (not <strong>actionIndex()</strong>), the Controller will use Zend_Lucene. In order to do so, this script needs access to the Zend files, so import the contents of the <strong>vendors</strong> directory at the top of this script, just before the class definition begins:</p>
<pre>Yii::import('application.vendors.*');</pre>
<p>Then, include the <strong>Lucene.php</strong> page, found within the Zend Framework <strong>Search</strong> folder:</p>
<pre>require_once('Zend/Search/Lucene.php');</pre>
<p>Now this Yii Controller can create objects of type <span style="text-decoration: underline;">Zend_Search_Lucene</span>, which is defined in that file. The actions will use that object type to perform the searches. To start, the index action just renders the index View:</p>
<pre>public function actionIndex()
{
    $this-&gt;render('index');
}</pre>
<p>Presumably the index View file just shows the search form. The search form, by the way, should have an action attribute of <span style="text-decoration: underline;">www.example.com/index.php/search/search</span>, so that it calls the search action of the search Controller. The form should contain a text input with the name <em>terms</em>.</p>
<p>The update action would be used by an administrator to update the search database. Perhaps it&#8217;d be called automatically after some content is generated or once per hour or day. It would destroy the existing search database and then invoke the <strong>actionCreate()</strong> method. The Lucene database can&#8217;t just be updated for whatever content changed; you need to destroy and recreate it instead. It really wouldn&#8217;t matter what View this action renders, depending upon what you want the admin to see. Maybe the View would just show a message indicating that the database has been updated.</p>
<p>The create action is an important one, and is where real knowledge of Lucene comes into play. The shell of it would look like so:</p>
<pre>public function actionCreate() {
    $index = new Zend_Search_Lucene($this-&gt;_indexFile, true);
    // Add documents to the database.
    $index-&gt;commit();
    $this-&gt;render('create');
}</pre>
<p>First, a <span style="text-decoration: underline;">Zend_Search_Lucene </span>object is created (again, this is where Yii is making use of a class defined outside of Yii; it&#8217;s a sweet thing). The first argument provided when creating the object is the location of the database files. This is represented by the Controller variable, accessible in <strong>$this-&gt;_indexFile</strong>. The second argument indicates that a fresh database should be created. Next up, you add content to the database. This is complicated and well beyond the scope of what I&#8217;m writing here. I&#8217;ll try to discuss this, in brief, in a separate post, but I&#8217;d recommend you read as much as you can online first. In a very minimalistic way, you could add a single HTML page to the search database by doing this:</p>
<pre>$url = 'http://www.example.com/index.php/page/show/id/1';
$doc = Zend_Search_Lucene_Document_Html::loadHTMLFile($url);
$index-&gt;addDocument($doc);</pre>
<p>Finally the database has to be saved, by invoking the commit() method. And then some View is rendered. As this action would also only be likely called by an administrator or cron, it doesn&#8217;t matter much what the View contains.</p>
<p>Lastly, there&#8217;s the search action. This action would check for search terms, run the search against the database, then send the results on to a View:</p>
<pre>public function actionSearch() {
    if (isset($_GET['terms'])) {
        $index = new Zend_Search_Lucene($this-&gt;_indexFile);
        $results = $index-&gt;find($_GET['terms']);
        $this-&gt;render('search', array('results' =&gt; $results));
    } else {
        $this-&gt;render('index');
    }
}</pre>
<p>First the method checks for the presence of search terms in the URL. Then it creates a <span style="text-decoration: underline;">Zend_Search_Lucene</span> object, which is necessary for both creating and using the search database. This time only the location of the search database is passed when creating the object. The object&#8217;s <strong>find()</strong> method is invoked for performing the search (it can be that simple!). Then the search View is rendered, passing it the results. If no search terms were passed to this page, the index View is rendered instead. As for the search results View, a basic version to get you started might look like this:</p>
<pre>&lt;h2&gt;Search Results for "&lt;?php echo CHtml::encode($_GET['terms']); ?&gt;"&lt;/h2&gt;
&lt;?php if ($results): ?&gt;
    &lt;?php foreach($results as $result): ?&gt;
        &lt;p&gt;&lt;?php echo CHtml::encode($result-&gt;title); ?&gt;&lt;/p&gt;
    &lt;?php endeach; ?&gt;
&lt;?php else: ?&gt;
    &lt;p class="error"&gt;No results matched your search terms.&lt;/p&gt;
&lt;?php endif; ?&gt;</pre>
<p>That&#8217;s largely the logic and structure of a search results View. It displays the provided search terms and checks for results. If there were some, each result title is printed. In a real application, you&#8217;d likely link the title to a URL or whatever but I don&#8217;t want to get too messy here. If you <strong>print_r() $result</strong>, you&#8217;ll see a bunch of information there that you can use.</p>
<p>So that&#8217;s the steps you need to take to get started using Zend_Lucene within your Yii application. These steps provide functionality; mastering Lucene is how you make this more professional. I&#8217;ll try to write more about defining a Lucene search database in subsequent posts towards that end. If you have any comments, questions, or requests, let me know.</p>
<p>Thanks,</p>
<p>Larry</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2009/12/05/integrating-zend_lucene-with-yii/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Configuring FCKEditor for Yii-Driven Sites</title>
		<link>http://blog.dmcinsights.com/2009/12/01/configuring-fckeditor-for-yii-driven-sites/</link>
		<comments>http://blog.dmcinsights.com/2009/12/01/configuring-fckeditor-for-yii-driven-sites/#comments</comments>
		<pubDate>Tue, 01 Dec 2009 06:56:03 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[fckeditor]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[wysiwyg]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=767</guid>
		<description><![CDATA[I&#8217;ve created three Yii-driven PHP sites in the past few months, each of which required an administrative area to dynamically manage the site&#8217;s content. Much of the content can contain some HTML, including media (images, videos, etc.), typography, lists, and so forth. So that non-technical people can create nice-looking HTML, I use a Web-based WYSIWYG [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve created three <a href="http://www.yiiframework.com">Yii</a>-driven PHP sites in the past few months, each of which required an administrative area to dynamically manage the site&#8217;s content. Much of the content can contain some HTML, including media (images, videos, etc.), typography, lists, and so forth. So that non-technical people can create nice-looking HTML, I use a Web-based WYSIWYG editor like <a href="http://ckeditor.com/">FCKEditor</a> or <a href="http://tinymce.moxiecode.com/">TinyMCE</a>. Getting either to work within the Yii environment isn&#8217;t too hard, once you know what to do. But because assembling the disparate parts can be tricky, I outline the specific steps and requirements in this post. Note that the post does assume <a href="http://blog.dmcinsights.com/series/learning-the-yii-framework/">familiarity with Yii</a>.<span id="more-767"></span>The first thing you&#8217;ll need to do is <a href="http://ckeditor.com/download">download FCKEditor</a>. FCKEditor has been updated to a newer version called CKEditor. The two are not interchangeable but the older FCKEditor is still maintained and available. You may want to compare the two before proceeding, and also be certain to review the documentation to determine whether or not you need to purchase a license.</p>
<p>Once the FCKEditor package is downloaded, you&#8217;ll need to expand the ZIP or TAR.GZ file (you can download the package in either format). The resulting folder is called just <strong>fckeditor</strong> and can be moved, in its entirety to the root of your Web directory. (You don&#8217;t actually need all the files in the folder but it&#8217;s fine to have them on the server.) Later on I&#8217;ll discuss configuring the editor but for now let&#8217;s move on to the Yii side of things.</p>
<p>Next, download the <a href="http://www.yiiframework.com/extension/fckeditor-integration/">FCKEditor integration widget</a> for Yii. Expand the downloaded ZIP file and you&#8217;ll end up with another folder called just <strong>fckeditor</strong>. Move this entire folder to your application&#8217;s extensions directory (i.e., <strong>protected/extensions</strong>).</p>
<p>To use the FCKEditor in a form, you&#8217;ll need to edit the <strong>_form.php</strong> file for the particular View. For example, on one site I edited <strong>protected/views/page/_form.php</strong>, as the admin created the site&#8217;s content through a Page Model. By default the form will have a standard textarea for every TEXT type MySQL column:</p>
<pre>&lt;?php echo CHtml::activeTextArea($model,'content',array('rows'=&gt;6, 'cols'=&gt;50)); ?&gt;</pre>
<p>To use FCKEditor instead of the textarea, invoke the integration widget by replacing that code with this:</p>
<pre>&lt;?php $this-&gt;widget('application.extensions.fckeditor.FCKEditorWidget',array(
 'model'     =&gt;  $model,
 'attribute' =&gt; 'content',
 'height'    =&gt;  '600px',
 'width'     =&gt;  '100%',
 'fckeditor' =&gt;  dirname(Yii::app()-&gt;basePath).'/htdocs/fckeditor/fckeditor.php',
 'fckBasePath' =&gt; Yii::app()-&gt;baseUrl.'/fckeditor/')
 ); ?&gt;</pre>
<p>That code starts by saying a widget should be rendered in this place, specifically the <em>FCKEditorWidget</em>. That widget gets passed an array of values to configure it. To start, pass the Model, which Yii stores in the <strong>$model</strong> variable by default (in older versions of Yii, you would have seen a specific Model name there instead, like <strong>$page</strong>). The <em>attribute</em> element is the name of the Model attribute, which will likely also match the field name in the database. Those two values are the most important and must be correct for each form. The height and width are obvious. The next two values point to the location of the FCKEditor on the server. If you moved those files into the root Web directory, still in the <strong>fckeditor</strong> folder, these lines will work just fine (assuming <strong>htdocs</strong> is the root Web directory).</p>
<p>So those steps are pretty easy to understand and will get you a working WYSIWYG editor in no time. But you&#8217;ll likely need to tweak how the FCKEditor behaves, too, which is much more complicated. For starters, if you want to allow the admin to upload files to the server, like images or videos, you&#8217;ll need to enable the file manager. Presumably you&#8217;ll be using PHP for this purpose. In that case, open <strong>fckeditor/editor/filemanager/connectors/php/config.php</strong> in your text editor or IDE. This tool is disabled by default, thanks to this line:</p>
<pre>$Config['Enabled'] = false ;</pre>
<p>To enable file management, this variable must be assigned a true value. You <span style="text-decoration: underline;">could</span> do this:</p>
<pre>$Config['Enabled'] = true ;</pre>
<p>but that&#8217;d be terribly insecure, as it&#8217;d allow ANYONE to manipulate files on the server (shudder). You could use Apache&#8217;s HTTP Authentication to restrict access to the <strong>filemanager/connectors/php</strong> directory, but then you&#8217;d have a separate, secondary login system from your application&#8217;s login system. So instead, I tap into the Yii user management system here. Presumably an administrator has to login in order to update the content. If so, then they&#8217;ll have a session, by default named <em>PHPSESSID</em>. So you can check for a cookie:</p>
<pre>$Config['Enabled'] = isset($_COOKIE['PHPSESSID']) ;</pre>
<p>Of course, that would also allow logged-in non-administrative users to use the file manager, too. So another clause needs to be added to this conditional. What that clause is will depend upon your situation but will likely involve something stored in <strong>$_SESSION</strong>. For example, perhaps your content admin account has a user name of &#8216;moderator&#8217;. If so, then you could use this code to verify that the user should be allowed to use the file manager:</p>
<pre>session_start();
$Config['Enabled'] = (isset($_COOKIE['PHPSESSID']) &amp;&amp; (in_array('moderator', $_SESSION))) ;</pre>
<p>A couple of explanations: First, you have to start the session in this file, as it won&#8217;t otherwise be started. Second, Yii stores values in the <strong>$_SESSION</strong> array using a somewhat more secure technique. Instead of just having <em>name</em> =&gt; <em>value</em>, Yii prepends a hash of random characters to the name of each element in <strong>$_SESSION</strong>. The user&#8217;s name, therefore, might be stored in session like so:</p>
<pre>$_SESSION['a4d58ee27e5fb1f0827af0550771ea7e__name'] = 'moderator';</pre>
<p>Because of this, you can&#8217;t just easily check the value of, say, <strong>$_SESSION['name']</strong>, which is why I use <strong>in_array()</strong> in the above code.</p>
<p>Also, while you&#8217;re editing the <strong>config.php</strong> file, you may want to change the name of the <em>UserFilesPath</em>:</p>
<pre>$Config['UserFilesPath'] = '/something_clever/' ;</pre>
<p>This is where the user-uploaded files will be stored. This value will appear in the HTML source when those files are used on a page, so this isn&#8217;t a huge security benefit, but it&#8217;s an easy step to take. You&#8217;ll need to create the corresponding folder, too.</p>
<p>Next thing up, you&#8217;ll likely want to configure the FCKEditor as it&#8217;ll behave in the Web browser. To do so, you&#8217;ll edit the FCKEditor configuration file (<strong>fckeditor/fckconfig.js</strong>). There are a ton of options there, best described in the <a href="http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options">FCKEditor manual</a>. For example, you can set the <em>EditorAreaCSS</em> and <em>EditorAreaStyles</em> values so that the FCKEditor window uses the same styling as your whole site. I also normally edit the toolbarsets so that limited options are presented to the end user (thereby keeping them from making a mess of the site).</p>
<p>You can also change some FCKEditor settings on the fly when you invoke the Yii widget. To do so, pass a <em>config</em> element with an array of values when calling the widget:</p>
<pre>&lt;?php $this-&gt;widget('application.extensions.fckeditor.FCKEditorWidget',array(
'model'     =&gt;  $model,
'attribute' =&gt; 'content',
'height'    =&gt;  '600px',
'width'     =&gt;  '100%',
'fckeditor' =&gt;  dirname(Yii::app()-&gt;basePath).'/htdocs/fckeditor/fckeditor.php',
'fckBasePath' =&gt; Yii::app()-&gt;baseUrl.'/fckeditor/'),
'config' =&gt; array('EditorAreaCSS'=&gt;Yii::app()-&gt;baseUrl.'/css/other.css')
); ?&gt;</pre>
<p>You might do this to change the styling of a specific FCKEditor window, as in that code.</p>
<p>Finally, consider that the Views, by default, echo out Model data using the <strong>CHtml::encode()</strong> method. This is a logical and necessary security feature, as it prevents cross-site scripting attacks (XSS). However, it also makes the FCKEditor-generated HTML completely useless (it&#8217;s the equivalent of PHP&#8217;s <strong>htmlspecialchars()</strong> function). A logical work-around is to use PHP&#8217;s strip_tags() instead, providing it with the optional second argument of allowable tags:</p>
<pre>echo strip_tags($model-&gt;content, '&lt;p&gt;&lt;a&gt;&lt;div&gt;&lt;ul&gt;&lt;ol&gt;&lt;li&gt;&lt;span&gt;');</pre>
<p>Okay, so there&#8217;s that then. I think that&#8217;s everything you should know to get going with FCKEditor and Yii. Let me know if you have any questions or would like me to write up any other subject. As always, thanks for reading what I have to say.</p>
<p>Larry</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2009/12/01/configuring-fckeditor-for-yii-driven-sites/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The DooPHP Framework</title>
		<link>http://blog.dmcinsights.com/2009/11/22/the-doophp-framework/</link>
		<comments>http://blog.dmcinsights.com/2009/11/22/the-doophp-framework/#comments</comments>
		<pubDate>Sun, 22 Nov 2009 22:23:43 +0000</pubDate>
		<dc:creator>Larry</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[mvc]]></category>

		<guid isPermaLink="false">http://blog.dmcinsights.com/?p=727</guid>
		<description><![CDATA[I&#8217;ve been writing a lot about the Yii framework for Web development using PHP. I&#8217;m a big fan of it and expect I&#8217;ll be using it for some time to come. I recently came across DooPHP, which I think is also worth consideration (although I haven&#8217;t yet used it myself). Besides having a really sharp [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been writing a lot about the Yii framework for Web development using PHP. I&#8217;m a big fan of it and expect I&#8217;ll be using it for some time to come. I recently came across <a href="http://www.doophp.com/">DooPHP</a>, which I think is also worth consideration (although I haven&#8217;t yet used it myself). Besides having a really sharp Web site, some of the strengths of DooPHP seem to be:</p>
<ul>
<li>GUI tools for auto-generating code</li>
<li>Many types of caches for improved performance</li>
<li>support for replicated databases (great for demanding sites)</li>
<li><a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST support</a></li>
</ul>
<p>I don&#8217;t plan on changing frameworks for a while, as I&#8217;m quite pleased with Yii, but if you&#8217;re looking for a new PHP framework, you may want to consider DooPHP as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.dmcinsights.com/2009/11/22/the-doophp-framework/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
