How not to develop on the Zend Framework
Hello! The other day, the work turned up - to add the site to zend framework. The programmer who began to develop this project did not have time in time, did not fulfill the requirements of the customer, and as a result it was replaced. When I opened the source code for the first time, I was horrified by the number of errors that the developer made, and he assured the customer that he was an experienced specialist. Next, I will try to talk about some of the mistakes that he made. This material will be useful to beginner ZF-programmers, as an instruction on how to do it is impossible. I will also talk about some points that are not directly related to the framework, but also are vivid examples of the ignorance of the developers.
Using the built-in methods of the model, instead of writing your own
The first thing that immediately caught my eye was the execution of queries through the model object directly in the controller code, which completely erases all the advantages of MVC. To make it clear what I’m talking about, I’ll give an example of source code:
$model = new Model();
$some_data = $model->fetchAll(array('field1 = ?' => 1, 'field2 = ?' => 0));
$all_data = $model->fetchAll();
Colored with dumpz.orgSuch a mixing of logic was in every controller file, which greatly interfered with reading the code and fixing errors, some not obvious dependencies constantly appeared, important data was overwritten. Accordingly, this should not be done in any case, even if you are developing a small project.
Accessing global arrays directly instead of accessing the Request object
ZF has a very convenient wrapper over global variables in the form of an object of the Zend_Controller_Request_Http class. This class provides us with a lot of opportunities for accessing data and is used by the framework in the process of dispatching. Therefore, do not neglect the use of the request object.
Lack of context-switching when necessary
To implement the processing of ajax requests, you can use the so-called context-switch which is a convenient way to change the response format. If you use json, then in the init method of the controller, set something like the following:
$this->_helper->AjaxContext()->addActionContext('ajax-handler', 'json')->initContext('json');
Colored with dumpz.orgNow all the data transferred to view in the ajax-handler action will be converted to json format. This method is preferable to disabling view and manually converting data to json.
Neglecting using Zend_Form and Zend_Validate
You should not use php functions for data validation, as ZF has very convenient validators that can be combined into groups and put on processing certain form fields. Using ZF-validators you reduce the chance that you will miss something and thereby make your applications more stable.
Lack of data validation
You must check all the data you received from the user, and you must check it in the context of the user's access rights, for example, if you write adding / removing materials from the favorites list and you have js-functions like the ones below, then you should do very well to think:
function addObject(object_id, user_id) {
if (user_id > 0) {
$.get('/realestate/favorite/oid/'+object_id+'/uid/'+user_id, function () {
$("#addfavorite"+object_id).hide();
});
}
}
function removeObject(object_id, user_id) {
if (user_id > 0) {
$.get('/profile/removefavorite/oid/'+object_id+'/uid/'+user_id, function () {
$("#removefavorite"+object_id).hide();
});
}
}
Colored with dumpz.orgAs it turned out later on server-side user_id was not checked for equality with the identifier of the current user, which is a serious vulnerability
Using controller class hierarchies instead of ACLs
Almost every site has several access levels: guests, users, administrators, etc. In order to control the access rights of statuses to certain parts of the site, hierarchies of controllers were used. Those. a parent class was created for admin controllers, a class for the rest of the controllers, and something like this was executed in this parent class:
public function preDispatch() {
// А вот пример глобальных переменных *fail*
if (!empty($_REQUEST['session'])) {
session_id($_REQUEST['session']);
} else {
$auth = Zend_Auth::getInstance();
if (!$auth->hasIdentity()) {
$this->_redirect('backoffice/auth/login');
}
}
}
Colored with dumpz.orgWhy is this approach bad? Firstly, the redistribution of access rights is very difficult. Secondly, it’s hard to maintain multiple roles. You can go on
for a long time. For these purposes in ZF, there is an excellent tool for creating access control lists or ACLs. Personally, I use a small plugin that checks the access rights of a given user for the requested action / controller during the dispatching process. This method allows you to create access rights in a simple, easily modifiable list, like this:
//Добавляем роли
$this->addRole('guest');
$this->addRole('user', 'guest');
$this->addRole('manager', 'user');
//Добавляем ресурсы
$this->add(new Zend_Acl_Resource('guest_allow'));
$this->add(new Zend_Acl_Resource('index/index'),'guest_allow');
$this->add(new Zend_Acl_Resource('index/registration'),'guest_allow');
$this->add(new Zend_Acl_Resource('error/error'),'guest_allow');
$this->add(new Zend_Acl_Resource('user_allow'));
$this->add(new Zend_Acl_Resource('index/logout'),'user_allow');
$this->add(new Zend_Acl_Resource('project/index'),'user_allow');
$this->add(new Zend_Acl_Resource('task/index'),'user_allow');
$this->add(new Zend_Acl_Resource('task/complete'),'user_allow');
$this->add(new Zend_Acl_Resource('task/assigned'),'user_allow');
$this->add(new Zend_Acl_Resource('manager_allow'));
$this->add(new Zend_Acl_Resource('project/add'),'manager_allow');
$this->add(new Zend_Acl_Resource('task/add'),'manager_allow');
$this->add(new Zend_Acl_Resource('index/add-user'),'manager_allow');
//Выставляем права, по-умолчанию всё запрещено
$this->deny(null, null, null);
$this->allow('guest', 'guest_allow', 'show');
$this->allow('user','user_allow', 'show');
$this->allow('manager','manager_allow', 'show');
Colored with dumpz.orgFiltering data in the form fields responsible for entering a password
Never, never filter the data that comes from the password field! This is fraught with long attempts to find the reason why users can not log in. In my case, the reason was the following:
$f = new Zend_Filter_StripTags();
$pwd = $f->filter($this->_request->getPost('pwd'));
Colored with dumpz.orgGiven that passwords should be stored in an encrypted form and should never be displayed, the presence of tags or spaces in them can in no way lead to vulnerabilities, respectively, and filtering will do nothing
It is necessary to provide for localization in advance
Often, for some reason, the localization of a project is placed in the background, i.e. First, we’ll fix everything, and then we’ll tighten the localization. This is a big mistake, because then it will be very difficult to fasten it. It will be necessary to find all non-localized strings, and this is a very lengthy process. It is much easier to immediately correctly process strings that require multilingualism.
Opt out of ViewHelpers
You should always use the url and baseUrl view helpers to form the url and paths to static resources. This is important because you cannot be sure how the application will be located by other developers. In our case, the paths were formed as if the application was at the root of the host, which created a number of problems when deploying to my machine.
Using text constants instead of boolean variables
In conclusion, I want to talk about an interesting way to replace logical variables with strings. In the code, I found something like this:
if ($a > $b)
$this->view->result = 'ok'
else
$this->view->result = 'fail';
Colored with dumpz.orgNo further explanation is required here, I think the last line very eloquently does it for me.
Conclusion
This is not a complete list of errors that were found in the process of finalizing the project, but I described the main ones. I hope that someone reading this material will write better code. Thanks for attention!