PHP Tweaks :)

echo
  • Outputs one or more strings separated by commas
  • No return value
    e.g. echo "String 1", "String 2"
print
  • Outputs only a single string
  • Returns 1, so it can be used in an expression
    e.g. print "Hello"
    or, if ($expr && print "foo")
print_r()
  • Outputs a human-readable representation of any one value
  • Accepts not just strings but other types including arrays and objects, formatting them to be readable
  • Useful when debugging
  • May return its output as a return value (instead of echoing) if the second optional argument is given
var_dump()
  • Outputs a human-readable representation of one or more values separated by commas
  • Accepts not just strings but other types including arrays and objects, formatting them to be readable
  • Uses a different output format to print_r(), for example it also prints the type of values
  • Useful when debugging
  • No return value

Creating Behaviors on Yii Framework

Behaviors are a good alternative to subclassing when it comes to sharing functionality among classes.
Yii Behaviors are a very elegant way to reuse functionality on the class level even though PHP itselfonly added Traits(a very close concept to Yii Behaviors) on version 5.4.

Creating a simple Behavior

p.s.: save this as SomeBehavior.php under protected/extensions
class SomeBehavior extends CBehavior{
    public function sayHello(){
        echo "hello"; 
    }
}

Using that Behavior in your class

p.s.: just using an AR model for example's sake; it could be any class at all.
class Person extends CActiveRecord{
    public function behaviors(){
        return array(
            'foo'=>array(
                 'class'=>'application.extensions.SomeBehavior'
        ));
    }
now, if you ever instantiate your Person class, you can call method sayHello() as if it had it!!
$p = new Person();
$p->sayHello();//'hello'

Extra Points: parameterizing (setting attributes to) Behaviors

You can also customize the behavior for each class that's using it. For example, say you have a behavior that you want to add a little flexibility to:
class CommunicateBehavior extends CBehavior{
    public $extraPolite=false;
    public function askQuestion($question){
        if($this->extraPolite){
            echo "Excuse me, sir, ".$question." ?";
        }else{
            echo $question." ?";
        }
    }
}
Then you can use it differently in each other class, for example:
class Brazilian extends Person{
    public function behaviors(){
        return array(
            'comm'=>array(
                'class'=>'path.to.CommunicationBehavior',
        ));
     }
}
or, for an Englishman:
class Englishman extends Person{
    public function behaviors(){
        return array(
            'comm'=>array(
                'class'=>'path.to.CommunicationBehavior',
                'extraPolite'=>true
        ));
     }
}
and each would ask a question in a different way:
$b = new Brazilian;
$b->askQuestion("what's the time");//what's the time?
but
$e = new Englishman;
$e->askQuestion("what's the time");//excuse me sir, what's the time?
This is really, really beautiful and powerful stuff. Always glad to have chosen Yii as my framework of choice.
w.i.p.: This is a work in progress. More content will hopefully be added for this.

SEPARATE FRONT AND ADMIN PANEL IN YII-FRAMEWORK

If you are not familiar with events & behaviors yet, then please, read this tutorial to get basic knowledge about using them.
First, create a new web application with yiic tool. I used an application generated by Yii 1.1.1, but everything described below should work with other versions of Yii Framework (probably with minor changes).
Now let’s modify some directories’ structure to separate front-end and back-end related files.
Since application’s ends usually use the same models, but different controllers and views, we will separate them by creating two subdirectories under protected/controllers and protected/views directories:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
webroot/
    themes/
        bootstrap/
            /front
                /layouts
                    column1.php
                    column2.php
                    main.php
                /site
                   /pages
                       about.php
                   contact.php
                   error.php
                   index.php
                   login.php
    protected/
        components/
            Controller.php
            UserIdentity.php
        controllers/
            /front
                SiteController.php
            /back
                SiteController.php
        views/
            /back
                /layouts
                    column1.php
                    column2.php
                    main.php
                /site
                    error.php
                    index.php
                    login.php
Front-end SiteController and all front-end views are files generated by yiic tool. You have to create back-end SiteController and back-end views by yourself (or just copy and modify front-end ones).
Now let’s create different config files for both ends. Since these files usually have much in common, we will “inherit” them from the main.php config:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
return CMap::mergeArray(
    require(dirname(__FILE__).'/main.php'),
    array(
        'theme' => 'bootstrap',
        'components'=>array(
            'urlManager' => array(
            'urlFormat' => 'path',
            'showScriptName' => false,
            'rules' => array(
                '<controller:\w+>/<id:\d+>' => '<controller>/view',
                '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',
                '<controller:\w+>/<action:\w+>' => '<controller>/<action>',
            ),
        ),
        )
        // Put front-end settings there
    )
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return CMap::mergeArray(
        require(dirname(__FILE__) . '/main.php'), array(
            'components' => array(
                /*
                'urlManager' => array(
                    'urlFormat' => 'path',
                    'showScriptName' => false,
                    'rules' => array(
                        'backend' => 'admin/login',
                        'backend/<_c>' => '<_c>',
                        'backend/<_c>/<_a>' => '<_c>/<_a>',
                    ),
                ),
                 */
            )
        )
);
By default, Yii will try to find controllers and views in protected/controllers and protected/views directories respectively. We have to change this behavior and force Yii to search controllers and views in the “back” or “front” subdirectories depending on the currently running end.
Actually we can do it in the -end’s config file by setting “viewPath” and “controllerPath” properties, but what if we are going to have some modules like News, Articles, etc.? We’ll need to set these properties for them too. We can also have some back-end modules which don’t need such separation.
Here comes the Yii magic. In protected/components directory create a file “WebApplicationEndBehavior.php” with the following contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class WebApplicationEndBehavior extends CBehavior
{
    // Web application end's name.
    private $_endName;
  
    // Getter.
    // Allows to get the current -end's name
    // this way: Yii::app()->endName;
    public function getEndName()
    {
        return $this->_endName;
    }
  
    // Run application's end.
    public function runEnd($name)
    {
        $this->_endName = $name;
  
        // Attach the changeModulePaths event handler
        // and raise it.
        $this->onModuleCreate = array($this, 'changeModulePaths');
        $this->onModuleCreate(new CEvent($this->owner));
  
        $this->owner->run(); // Run application.
    }
  
    // This event should be raised when CWebApplication
    // or CWebModule instances are being initialized.
    public function onModuleCreate($event)
    {
        $this->raiseEvent('onModuleCreate', $event);
    }
  
    // onModuleCreate event handler.
    // A sender must have controllerPath and viewPath properties.
    protected function changeModulePaths($event)
    {
        $event->sender->controllerPath .= DIRECTORY_SEPARATOR.$this->_endName;
         
  
        if ($event->sender->theme !== null)
            $event->sender->viewPath = $event->sender->theme->basePath.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$this->_endName;
        else
            $event->sender->viewPath .= DIRECTORY_SEPARATOR.$this->_endName;
    }      
}
Now add some lines to the main config file:
webroot/protected/config/main.php:
1
2
3
4
5
6
7
8
9
10
11
12
'import' => array(
    'application.models.*',
    'application.components.*',
),
...
...
...
'behaviors' => array(
    'runEnd' => array(
       'class' => 'application.components.WebApplicationEndBehavior',
    ),
),
Now our application has a new method runEnd (to run one of the application’s ends) and a new event onModuleCreate. By raising this event from a web module we can change modules’ properties. Controllers and views paths are changed in the attached handler “changeModulePaths”.
If you have a module, which should use different controllers and views for different ends, then just modify it’s init() method:
1
2
3
4
5
6
7
8
9
10
11
protected function init()
{
    // ...   
  
    // We can configure our module depending on the value
    // of Yii::app()->endName.
    $this->foo = (Yii::app()->endName == 'front') ? 'bar1' : 'bar2';
  
    // Raise onModuleCreate event.
    Yii::app()->onModuleCreate(new CEvent($this));
}
Note that in this case the module’s controllers and views paths must be organized as shown before.
If a module doesn’t need a separation to back-end and front-end controllers and views, then just omit the onModuleCreate event’s raising.
Finally, let’s protect back-end by creating a parent controller for all back-end controllers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class BackEndController extends CController
{
    public $layout='layout_name';
    public $menu=array();
    public $breadcrumbs=array();
  
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
  
    public function accessRules()
    {
        return array(
            array('allow',
                'users'=>array('*'),
                'actions'=>array('login'),
            ),
            array('allow',
                'users'=>array('@'),
            ),
            array('deny',
                'users'=>array('*'),
            ),
        );
    }
}
webroot/protected/controllers/back/SiteController.php must extend this controller to perform access checking.
Everything’s done. New index.php and backend.php files are:
1
2
3
4
5
6
7
8
9
$yii = dirname(__FILE__).'/../yii/framework/yii.php';
$config = dirname(__FILE__).'/protected/config/front.php';
  
// Remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);
  
require_once($yii);
Yii::createWebApplication($config)->runEnd('front');
1
2
3
4
5
6
7
8
9
$yii = dirname(__FILE__).'/../yii/framework/yii.php';
$config = dirname(__FILE__).'/protected/config/back.php';
  
// Remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);
  
require_once($yii);
Yii::createWebApplication($config)->runEnd('back');
Back-end url rules:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AddDefaultCharset utf-8
 
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
 
# Make the backend accessible via url: http://site/backend.
RewriteRule ^backend backend.php
 
# If a directory or a file exists, use it directly.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
 
# Otherwise forward it to index.php.
RewriteRule . index.php
Then, in the back.php config file un-comment url manager component:
Now you can generate beautiful urls using CHtml::link() method.
If you have a module (e.g. news), then you’ll need to add 3 more rules before existing ones:
1
2
3
'backend/news'=>'news',
'backend/news/<_c>'=>'news/<_c>',
'backend/news/<_c>/<_a>'=>'news/<_c>/<_a>',
Also, you still can add own rules:
1
'backend/news/<id:\d+>'=>'news/newsReport/update',
But make sure that you insert these rules before news rules containing and . Otherwise the latter rules will be used instead.