In object oriented programming, interfaces are commonly used to implement more flexible software. Interfaces are very useful to extend and improve a software application. While designing a software its a good practice to use interfaces where applicable.
Lets think about a simple CricketPlayer class (Figure 1) with a method play(). There can be several classes and methods using CricketPlayer class. Think we need to give the support for Football player as well within this software. Then we need to repeat the wheel for new purpose(Figure 2).
Figure 1 : Initial cricket class
Figure 2 :Modification for support football player
But just think if we use common interface (Figure 3) for any type of player, we can easily get rid of the problem. Player interface will have play() method which may be different behaviour for different players and getSport() method which represent the type of the player (sport). CricketPlayer and FootballPlayer subclasses can implement their own behaviours according to related declarations in the Player interface. Coding to this interface will save us with very limited changes, while any kind of player class is going to use in our software. Using an interface software can be extend easily and will be more generic.
Figure 3 : Player interface
Recently I saw practically very useful place where interfaces are using. Most of the industry applications are using the MVC architecture to better maintainability of the code. There may have requirements to use the same applications with different DBMS for different clients. Using an interface with n-tier architecture, this support can be easily implemented.
Following diagram (Figure 4) shows the way of using this stratergy in n-tier architectural application. From the Controller classes which deals with request and responses while dealing with the business logic of the application, calls to the service classes to do specific data manipulation, transaction etc. Service classes calls to Dao classes which deals with data transactions. Service class doesn't directly use either MySqlUserDoa which has MySQL support or XQueryUserDoa which has XQuery support. It only care about BaseUserDao interface, and doesn't care about real object underling the BaseUserDao interface.
Figure 4 : Dao interface
Implementaion under this process is quite simple. Control class read configuration file (or from any other way) and get the Query Language supposed to use according to the requierments. Then create an object of supported Dao class and assigned to a variable of BaseUserDao (interface) type. After this initializing process of BaseUserDao type variable, it can be used by the service classes inside their functions.
BaseUserDao userDao;
if (queryLanguage == 'MySQL') {
userDao = new MySqlUserDao();
} else if (queryLanguage == 'XQuery') {
userDao = new XQueryUserDao();
}
Since application is not creating concrete classes (objects are dynamically created in run time and will not directly creating objects for any case), it will not be limited. And since it uses an interface, switching for a new DBMS can be achieved by only adding new file which inherit the interface and modify the object creating process(modify above code) and giving the new DBMS as parameter, using a configuration file or any other way.
Coding to interfaces, not to implementations principle is a good practice for a good software and it make our work more easy. If you are thinkng of creating extendable software application, this practice will very helpful. So, its time to play with interfaces. :)
Singleton pattern is under creational design patterns which deals with mechanisms of creating objects according to the situation. I recently used this pattern with PHP. It's very important to get to know use of this pattern.
Singleton pattern can be used for always keep one object of a specific class while an application is running. It save the memory which is going to allocate for new objects of the class in every where inside the application. Singleton practically used for logger classes which provide global logging access point whithout logging each and every application after the main loggin operation, configuration classes which consist configuration data of an application, access resources which are in shared mode like multi threading applications, Factory design pattern which dynamically create the objects etc.
It's very easy to implement singleton with PHP (as well as other languages). What we need to do is make a private constructor and control the creating object of this class by a public method. Following code snippet shows how to do this.
class MySingleton {
private static $instance = null;
public static function getInstance() {
if(is_null($this->instance)) {
$this->instance = new MySingleton($params);
}
return $this->instance;
}
private function __construct($params) {
// set parameters
}
public function doSomething() {
// some code
}
}
Since the constructor is private only the class itself can be access the constructor. getInstance method is accessible for anywhere and since that is a static method it can be used anywhere without creating an object of this class. This function handles the situations of the need of a new instance of MySingleton class by calling the private constructor if needed. Static variable is used because of the restriction of using non-static fields inside static functions.
In the places where needs to call doSomething method in MySingleton class, we call as,
MySingleton::getInstance()->doSomething()
or, if the functions inside the class is frequently used,
This is the last week we interact with the coding and documenting under GSoC 2012 official time line. In my last week I implemented the feature request simple sort for process list. And improved some tests for PMA_DisplayResults class.
In this week I did some improvements in tests of PMA_DisplayResults class. As the documentation, there were not much things to mention as details in Documentation.html. I only mentioned about the feature of displaying multiple results for queries as well as stored procedures. While I'm implementing the functions phpdoc comments and block comments were improved. So those things make me easy in documenting session. A feature request I proposed to do at the end of the last week was already fixed (My patch has been accepted for that). So I didn't involve with that.
Dieter has suggested to change the class structure for PMA_CommomFunctions class. After discussing in mailing list I decided to make any change after the GSoC 2012 session. I will be stop my further work related to code and documentation improvements under GSoC 2012 session by today.
In next week I'll submit my evaluation form. I wish all GSoCers who passed the mid evaluations to get success this Great event GSoC 2012 with Open source :)
Its more one week to do the remaining work of GSoC 2012 project. Last week I introduced new links inside information_schema database with modifying a previous code for showing links inside mysql database. As well support for MIME transformations in query results and VIEWs were implemented. More improvements related to VIEWs were done.
In this week I implement the feature request simple sort for process list. That was little older feature request and the description of the feature request itself suggested the fix. But with the time those files were changed and renamed. So I improve the behavior of server_status.php file to do the required thing. Since the sorting needs to specify the table, I used prcesslist table of information_schema database. Apart from these I did some test implementations for the functions in PMA_DisplayResults class.
In my next week I'm intending to improve some tests for PMA_DisplayResults classwhile doing any remaining minor improvements in my codes.
This is my 11th week of GSoC 2012 project. Its one more week to active work in code level. Last week I did some improvements on showing syntax highlighted SQL statement in PROCESSLIST table in information_schema database and introduce some links inside mysql database related to displaying data fields.
This week as the first task I introduce some links inside information_schema database. In previous week I implement same kind of thing but in more specific way to that task. So I change the previous implementation where it can be used to my new task. I changed the structure of the global variable $GLOBALS['mysql_schema_relation'] and renamed it as $GLOBALS['special_schema_links'] and renamed the file as special_schema_links.lib.php. The new structure of the array can be represented by following snippet.
$GLOBALS['special_schema_links'] = array(
// Database name is the major element
'mysql' => array(
// Table name
'db' => array(
// Column name
'user' => array(
// Main url param (can be an array where represent sql)
'link_param' => 'username',
// Other url params
'link_dependancy_params' => array(
0 => array(
// URL parameter name
// (can be array where url param has static value)
'param_info' => 'hostname',
// Column name related to url param
'column_name' => 'host'
)
),
// Page to link
'default_page' => 'server_privileges.php'
)
)
)
);
By using this array, links can be generated where needed specially inside schema like mysql, information_schema etc.
Next I moved into implement feature request for support MIME transformations in query results. Some of the query results which show only field with transformation and large data (blob fields etc) were not displayed their expected transformation in results. PMA has avoided that kind large data comparisons for SQL queries. So that, $where_clause parameter is not generating properly. So, assuming the length of the data fields will not same, I used the length of the data field to generate proper $where_clause and compare inside SQL query. Now query results display the expected out put with their transformations if any.
Then I moved into give the support for transformed data for VIEWs. In PMA the columns with any transformation are stored inside column_info table of phpmyadmin database. But the problem was these information were not stored in column_info table while creating VIEWs. So I did some changes to store needed details of transformations in VIEWs whie creating them by the link (create view) in browse mode of a result set. A new file called tbl_views.lib.php was introduce to place related functions.
As well when deleting a column, table, view or database the related information on transformations were not deleted from the column_info table. So I implement a function to delete relevant entries and placed the necessary places to do the work.
In my next week I intend to implement the feature request on simple sort for server_processlist.php.
This is my 10th week of GSoC 2012
project. In last week I implemented support for displaying results for
multiple SQL query statements and stored routines with multiple queries.
SQL query window can be used to both cases above.
There
were some issues in my previous code. So I first moved into fix one
issue. It takes bit more time to fix it than I thought.
Then I moved into next feature implementation. Mainly there were two parts in this feature.
1. Syntax highlighting for SQL in PROCESSLIST table in information_schema database
2. Relations inside mysql database
For
the first task, I used a new public property inside PMA_DisplayResults
class which contains all the details of columns which need to be apply
syntax highlight effect. So
that any other table field also can use this feature. Now the SQL field
is syntax highlighted in PROCESSLIST table.
Second
task is not related to actual relations with foreign keys, it means
just link data rows to corresponding pages where possible. As an
example, User column of db table in mysql database, can be linked to
edit user privileges of corresponding user according to the User column
value. A new file called mysql_schema_relation.lib.php has introduced
which have a global variable with all the column information of tables
which needs to be display as links where possible. From each element of
this variable (array), one link can be generated.
$GLOBALS['mysql_schema_relation'] = array(
'db' => array( // db table in mysql schema
'db' => array( // Db column in db table
// link_params array contains the get params,
// if array element is string, value of parameter is set as column value
// if any of array element is array, url param is the first data of the array and value is second data of the array
'link_params' => array('db'),
// This represent the page to link
'default_page' => 'index.php'
),
'user' => array( // User column of db table
'link_params' => array('username'),
'default_page' => 'server_privileges.php'
)
),
'proc' => array(
'db' => array(
'link_params' => array('db'),
'default_page' => 'index.php'
)
),
);
Above
array is represented as lowercase string array. So when comparing the
strings those need to be convert to lowercase. (strtolower() function
can be used.)
Now
there can be see several links inside mysql schema. In my next week
I'll implement the feature, linking from information_schema to phpmyadmin
schema.
In last week I did some improvements in PMA_DisplayResults class and in test cases. This week I moved into implementation of a feature request. Its on display results for the multiple queries.
Multiple query execution can be done by using routines ( stored procedures and stored functions) or just executing SQL statement with multiple queries at once. In PMA those could be done by using SQL query window or using routines tab under any table.
PMA was not supported for executing multiple queries using the SQL query window. While executing it shows the results for the last single query in the multiple SQL statement. That was due to use of mysql_query() function for any case. Since mysql extension is currently not support for the multiple query execution I used mysqli_multi_query() function under mysqli extension. It returns multiple result sets depending on the executed SQL statement. By iterating through the results I displayed this resulted tables in the same page in top to bottom manner. And I made it with very limited operations on the results in same page. Unless it becomes more complex and hard to handle the code. Currently there are two known issue.
1. After executing insert/ edit/ delete query using the query window, page does not show the query window back.
2. After executing multi query SQL statement, browse any table gives notices and some errors.
After those modifications I moved into routines. That was very cool feature in PMA. But it was not complete. For any routine it shows only one row from only one table. So I improve the behaviour of that feature to return expected outcome according to the routine. Now it shows even multiple results sets with all the needed rows.
In next week I try to fix those issues in SQL execution mode. And I'll implement new inline transformations for PMA as the next feature implementation.
I'm pretty sure that most of the GSoCers are very happy in the 8th week of their GSoC project. Because of the happiness of gone through the mid evaluation. In last week I break some functions into more sub functions and introduce global variables (class variables) and __get, __set magic methods for PMA_DisplayResults class with the suggestion of Michal. As well some improvements in tests also done.
According to last week modifications in PMA_DisplayResults class, initialization of the global variables in that class were done inside getTable() method. But as suggested from Michal, I did that initialization part in a separate method called setProperties() and call that function from the sql.php file just before call the getTable() method.
As well I found a issue with my code with the print view screen. I fix those issues both in vertical display mode and horizontal display mode. Then I move into improve unit tests. There were several errors in tests related to PMA_DisplayResults class due to my recent changes. The problem was, needed property values were not set at the run time of tests. I modified those tests to set those properties using magic __set() method. But that was not worked. After searching this issue, I found that, there is a problem with using php property_exists() method is not properly working with the magic methods. So that I introduce a new property $_property_array which contains all the previous properties except $_common_functions. Then I remove previous properties which represent in $_property_array and use php array_key_exists() method inside magic methods.
It takes lot of time to run the all tests in PMA. So that I try to run tests with individual file by giving arguments in command line. But it gives errors again and again due to missing required files and undefined variables, constants etc. After that, I edit phpunit.xml.dist file in order to just run the needed file. With that modification I could run those tests quickly. At the moment previous errors in tests related to PMA_DisplayResults class were fixed.
In next week I'll move into start the work I suggested after mid evaluation. I'll start implementation of display multiple query results.
This is seventh week of the GSoC 2012 projects. As well this week we need to submit our mid evaluation. So this is a key point of GSoC 2012. Last week I merge the code with master which had Alex's major changes related to his project. And did some modifications regarding mime default function. As well I started to write tests for PMA_DisplayResults class.
In this week I continued writing tests for PMA_DisplayResults class. While those improvements Michal assign me another two tasks.
1. Reduce the large number of parameters passing to functions in PMA_DisplayResults class by storing the variable states inside the class itself
2. Break some functions to more small functions
So, first I did the 2nd task. With that I introduce several sub functions by breaking _getTableHeaders(), _getTableBody() and getTable() functions.
Then I moved into 1st task. I stored the commonly used fields as private properties of the class. There are around 25 private fields in that class now. Since those properties are not initialized when creating the object of this class, most of them were initialized inside the getTable() function.
After I again moved into improving tests. At this point most of the existed tests were failed with the new changes. So I introduce __get() and __set() method for access any private field in PMA_DisplayResult class by out side. Then I use those functions to set private properties while improving tests. But still the failures are remaining in some tests. Apart from that setCommonFunctions() function was removed from PMA_DisplayResults class due not use of that method.
In next week I'll continue test cases as much a possible. But before that I'll do some modifications to PMA_DisplayResults class which was suggested by Michal recently.
In last week I almost finish the implementing class behavior of PMA_CommonFunctions.class.php but not committed due to some conflicts while merging the code to with master branch.
In this week also I got lots of conflicts while merging the code. Since the functions inside PMA_CommonFunctions class (in master common.lib.php file) were used in many places over the PMA the possibility of occurring a conflict is high. With the merge of Alex's code I again got many conflicts. As well I had to do more modifications because Alex has introduced several new files and they have used the functions in common.lib.php . After did those modifications I committed the code with 4 known issues in master. At the moment two are already fixed in master branch. Others are,
1. When the session expires, after click logout I get following notice,
Notice in ./libraries/relation.lib.php#35
Undefined index: controllink
2. When incorrect login, following warning raised,
Warning in ./libraries/dbi/mysqli.dbi.lib.php#97
mysqli_real_connect(): (28000/1045): Access denied for user 'root'@'localhost' (using password: YES)
After that I moved into finding the possibility of using PMA_mimeDefaultFunction() function which is currently inside the core.lib.php file as a global function, inside the PMA_DisplayaResults class. This function is only used by the functions in PMA_DisplayResults class indirectly with the help of a variable. I moved the PMA_mimeDefaultFunction() function from core.lib.php file to PMA_DisplayResults class and renamed as _mimeDefaultFunction(). The places where calls this function indirectly were modified by adding '$this->' suffix ($default_function = '_mimeDefaultFunction'; $this->$default_function()).
After those improvements I moved into implement tests for the functions in PMA_DisplayResults class. Yasitha has already implemented some tests for some functions. I started to continue that work and finished some tests.
In next week as the major task I'm intending to implement tests for remaining functions in PMA_DisplayResults class. If possible I'll be do some minor improvements in PMA_CommonFunction.class.php
This is the 5th week of my GSoC 2012 project. In last week I started to implement class behavior in common.lib.php file.
This week I finish remaining part of the implementing class behavior in common.lib.php file. Now the class is named as PMA_CommonFunctions and file is appeared as CommonFunctions.class.php. This class is used singleton pattern to avoid the creation of many objects by several places, which needs more memory. If any function or file with procedural codes contain more than one function inside PMA_CommonFunctions class the class object is assigned to a variable called $common_functions and used that variable to call needed functions inside that class. The classes which use the functions in PMA_CommonFunctions class, were modified by adding getter setter methods to get/set PMA_CommonFunctions class object.
I faced several problems while implementing this part. Some functions
were used more than 400 times in many files. All of those cannot be just
replaced due to some JS functions also appeared with same name. After completing those modifications, I could only see login screen on my browser. After login nothing displayed in my browser. Then according to guidance of Michal, I enabled write error log to file. So that I could debug the code. Then I found that the error was due to calling non-static methods in side static functions in some classes. Those were replaced by direct calling using the static method (PMA_CommonFunctions::getInstance()->someFunction()) instead of get the use of variable for store class instance.
Then the code was seems work fine. At the moment code is not committed yet due to some conflicts while merging the original PMA code. I'll be able to commit those changes as soon as after resolving them.
In next week I'll start to looking at the possibility of using PMA_mimeDefaultFunction() function (at the moment this is inside core.lib.php file) inside PMA_DisplayResults class as suggested by Marc. If possible unit tests were implement for the functions inside PMA_DisplayResults class.
In my third week I introduce a class behavior for display_tbl.lib.php
file. Now that file has appeared as DisplayResults.class.php and the
class inside it is named as PMA_DisplayResults.
Since
there was a task not completed in last week, I first completed that. It
is removing imported super globals from the functions. There were
several functions which used local variables which are imported from
$GLOBALS super global array. I removed them and directly used super
globals where needed. As well for the super globals which were not
change during one execution cycle of the PMA code, I assign them to
global variables in the PMA_DisplayResults class (local for that class).
That global variables are initialized inside constructor.
As
next task, I was moved into fix the bug which was occurred due to
breaking _getTableBody() function into subfunctions. It was very helpful
for me that Marc had already mentioned the bug is about breaking the
feature, which display a link for foreign table value. When I going
through the code I saw that was occurred due to incorrect method calling
of _getDataCellForNumericFeilds() function (not according to the
signature). Now the bug is fixed.
Then I moved into refactoring common.lib.php file. First I remove the HTML renderings inside the function it selves and return a string instead of that. As well some conventions are used according to my proposal. And the all functions names were modified by removing phrase 'PMA_'.
Within next week I'm intending to complete implementing class behavior of common.lib.php file. While refactoring DisplayResults.class.php file I have moved a function (PMA_mimeDefaultFunction) to common.lib.php file and now again move it to core.lib.php file for keep it as global function. I intend to again look at the possibility of get that function back to DisplayResults.class.php file.
In my 2nd week, I did some improvements relating to readability and usability of the code including breaking large functions into sub functions. After that step, display_tbl.lib.php file contained many sub functions.
In this week, I mainly focused on applying class behavior to the display_tbl.lib.php file. After getting the confirmation from Michal Čihař for the rough design of that PMA_DisplayResults class, I started my work.
First I search for the functions which are used by the outsiders. There were only two functions (PMA_getTable(), PMA_setConfigParamsForDisplayTable()) were used by sql.php file. Excepting those two functions, private key word was used as access modifier for all other existed functions. For constructor and the above two functions were appeared as public so that any outsider can access those. All existed function names were modified by removing 'PMA_' since they becomes to local functions. The file display_tbl.lib.php was renamed as DisplayResults.class.php .
Then I looked into, remove strings used for conditions in comparisons and introduce constants instead of that. There were many constants after doing this step. If there is any need for change the value of those strings, we can do it by just changing in one place. That is an advantage of using constant.
With these processes there were lots of code violations had been occurred. Most of them were fixed excluding some warnings which were due to very long comparisons inside if conditions.
Introducing global variables for the PMA_DisplayResults class is not finished yet. It will be done in next week. Unexpectedly there was a bug due to one of my previous commits which was pointed by Marc Densile. I am going to fix it in the next week. As well, I'll start to look into refactoring other files related to display query results. Mainly I'll focus on common.lib.php file.
PMA_DisplayResults class will contain the functions specific to the displaying
query results and handling special tables like `PROCESSLIST` which provides informations on current threads running in PMA. At the current code base, these functions are
appeared as global functions inside display_tbl.lib.php file. After
introducing PMA_DisplayResults class instead of this set of functions, the file
will be renamed as DisplayResults.class.php .
Class diagram (not the completed one) for the PMA_DisplayResults class
Following code snippet represent some rough implementation of the PMA_DisplayResults class,
class PMA_DisplayResults {
const DIRECTION_LEFT = 'left';
const DIRECTION_VERTICAL = 'vertical';
private $_db;
private $_table;
public function __construct($db, $table) {
$this->_db = $db;
$this->_table = $table;
}
public function getTable() {
// Related code for getTable function
$str .= $this->_getTableHeaders();
// end of the related code
}
private function _getTableHeaders() {
//code...
$msg = PMA_getMessage($this->_db, $this->_table);
//code...
}
}
As the only outsider accessed Display class, the sql.php file will be use,
This is my second week of GSoC 2012 project since I have started my coding before the official day. I have started to refactoring the file display_tbl.lib.php in PMA. Last week I remove the HTML rendering inside the functions it selves in this file. Some readability improvements also done.
In this week, I mainly focused on improving the readability of the code inside display_tbl.lib.php file. There were several function which having more than 200 code lines in this file. I broke those functions into sub functions in a way that each sub function do a specific portion for the bigger function. As well the sub functions were not having very large code blocks and those functions were placed just after the referring function. The names of these sub functions were little longer sometimes, because of they express valuable details for the developer at the first time.
In some places there were used similar code segments again and again
even in the same functions. Those similar code segments were moved into
new sub functions with the help of some additional parameters to
separate out the small differences. (PMA_getOperationLinksForVerticleTable, PMA_getColumnParams etc.)
I have mentioned those larger functions which were broken into sub functions with there names below.
PMA_getTableNavigation
PMA_getMoveBackwardButtonsForTableNavigation
PMA_getShowAllButtonForTableNavigation
PMA_getMoveFarwardButtonsForTableNavigation
PMA_getAdditionalFieldsForTableNavigation
PMA_getTableHeaders
PMA_getSortByKeyDropDown
PMA_getDataForResettingColumnOrder
PMA_getOptionsBlock
PMA_getFullOrPartialTextButtonOrLink
PMA_getFormForMultiRowOperations
PMA_getCommentForRow
PMA_isInSorted
PMA_getSortingUrlParams
PMA_getSortOrderLink
PMA_getDraggableClassForSortableColumns
PMA_getDraggableClassForNonSortableColumns
PMA_getTableBody
PMA_getUrlSqlQuery
PMA_getColumnParams
PMA_getVerticalDisplaySupportSegments
PMA_getModifiedLinks
PMA_getDeleteAndKillLinks
PMA_getPlacedLinks
PMA_getResettedClassForInlineEdit
PMA_getClassForDateTimeRelatedFields
PMA_getDataCellForNumericFeilds
PMA_getDataCellForBlobField
PMA_getDataCellForGeometryFields
PMA_getDataCellForNonNumericAndNonBlobFields
PMA_getVerticalTable
PMA_getOperationLinksForVerticleTable
PMA_getCheckBoxesForMultipleRowOperations
PMA_getTable
PMA_getOffsets
PMA_getSortParams
PMA_getSortedColumnMessage
PMA_setMessageInformation
PMA_getMultiRowOperationLinks
Apart from this I used some coding conventions for improve the readability. If the function is not too small one, an empty space were kept both after starting bracket of the scope and before ending
bracket of the scope. Not only for that, wherever I saw the codes are
seems packed and hard to read, I put empty lines between code segments. As well I followed the conventions suggested in my proposal.
At next week, I'm going to change my plans by giving the place to most
prioritized task at the moment. I am going to make the
display_tbl.lib.php file as a PHP class. As well I'm going to reduce
the usage of global variables which are used in most of the functions.
The strings used inside the functions for comparisons, will be replaced
with constants. The design for the class will be discussed with the
community on next week.
This is my first week of Google Summer of Code project which is Refactoring: Displaying query results under phpMyAdmin organization. Since I have my final examination, I started my work before the official day for start coding(21st May). It was a great experience that working with world wide open source community. That experience will be very helpful for me to manage my work in the upcoming weeks.
According to my project schedule, I was dealing with, refactoring the file "display_tbl.lib.php" in this week. That is a file with 22 global functions. Some of them are really huge functions. As well, though library files are suppose to provide functions which having logics(calculations, concatenation, manipulation etc), while being in a separate layer (data/model layer), this file have some HTML renderings inside themselves. And the naming convention of some methods were different from the phpMyAdmin (PMA) standards.
In this week, I modified the all the needed functions in "display_tbl.lib.php" file, to remove the HTML renderings inside themselves. Instead of that, that functions are now returning a string which having relevant HTML content for each function. Since inside some of functions are using the functions in "common.lib.php" file and some of them also had the same issue, I modified them as well. The unit tests for each also modified according to the changes I did in corresponding functions.
The names of most of the functions were changed due to changing of the behavior of some functions. Those names were changed with adding "get" phrase. I was also looked in to modify the code to keep necessary indentation, since it improve the readability of the code.
At next week, I'll more look into improve the readability of the code with adding the suggested conventions in my proposal. As well the huge functions will be braked in to several functions. The unit tests also will introduced as possible for those function.
I am very happy to say that I have been selected for a project in Google Summer of Code 2012. The project idea is "Refactoring: Display query results" which is under phpMyAdmin organization. In this project I'm going to refactor the code which is related to displaying query results and implement some feature requests.
Short description of my project:
In current code base of phpMyAdmin,
the code for displaying query results is not much reusable. To improve
the ability of re usability of the code, I have suggestions to refactor
the code with better approaches. The code is going to be improved by
applying Object Oriented Programming concepts.
Along with that, new features like support for displaying
results for multiple queries at once, introducing some more built-in
transformations where it is applicable will be considered.
Now I'm ready to walk with GSoC 2012 and hope to do a great job with phpMyAdmin developers and other GSoCERS. :)
TDD (Test Driven Development) concept is common practice of
most of the companies which involve with the long time projects. It
helps to keep the static behavior of functions over the development
period. The functions are implemented after implementing test methods
(unit tests) for that function. After implementing the function, run the tests and if any test is failing, again modify the function. This process repeats until the all tests are passing.
Test method is a function which check whether the specific behavior of a
function is correct. Any function can have at least two test methods. one
positive test and one negative test. Most probably a function may have
many test methods. The class in which all the test methods are placed is
called Test Case. By setting up a test suite for this class, the all
tests methods can be run at once.
In long time
projects like open source projects, the functions are frequently
modified by the developers during a long time. If the modified function
is not given the previous behavior of that function, the output for other places will be going wrong. It can be cause to failures. By
writing tests this problem can be reduced. Before and after modifying
the test cases, by running the tests, developer can be verify the
function behave correctly. As well by going through the test cases helps
even understand what actually function does.
To follow this TDD concept with Java, JUnit framework can be used. As
well with PHP, PHPUnit framework can be used. Let's do some experiments
with PHPUnit with Ubuntu OS.
Install PHPUnit framework
$sudo apt-get install phpunit
Now create your test case as a
sub class of 'PHPUnit_Framework_TestCase' having just one dummy method
to verify we can forward without issue.
<?php
require_once 'PHPUnit/Framework.php'; // installed file with phpunit framework which is located at : /usr/share/php/PHPUnit/
class MyClassTest extends PHPUnit_Framework_TestCase {
// This function just return true
function testCheckAllWorksFine() {
$this->assertTrue(true);
}
}
?>
Run this MyClassTest.php file as :
$phpunit MyClassTest.php
If
the result is as following (Fig.
1), you can proceed the work with phpunit.
I
have used following useful assert functions for this article :
assertTrue()
: Check whether the parameter is True
assertFalse()
: Check whether the parameter is False
assertEqual() : Match two
results
Start
work
1. Requirement : need a function which accept two strings
and return the length of the most lengthy string, if both arguments are
not strings return false
* First just declare your
function properly with arguments
<?php
class MyClass {
function getMaxLength ($str1, $str2) {
}
}
?>
* Now start
to write test methods,
a). Check the arguments
<?php
require_once 'MyClass.php';
require_once 'PHPUnit/Framework.php';
class MyClassTest extends PHPUnit_Framework_TestCase {
var $obj;
// This function calls before any test merhod run
function setUp() {
$this->obj = new MyClass();
}
// Positive : Test getMaxLength for correct arguments
function testGetMaxLengthForAtLeastOneIsString() {
$paramArray = array("str1", 2);
$result = $this->obj->getMaxLength($paramArray[0], $paramArray[1]);
$this->assertEquals(4, $result);
}
// Negative : Test getMaxLength for non of arguments are string
function testGetMaxLengthForNoStrings() {
$paramArray = array(array(1,2), 12.3);
$result = $this->obj->getMaxLength($paramArray[0], $paramArray[1]);
$this->assertFalse($result);
}
}
?>
The setUp() will call automatically
before running any test method. It can be used for create a fresh
objects, load data for databases if using etc. All test methods must be
start with 'test'. Using the function name which is going to use by
specific test method regardless of the length of the test method name,
will be increase the readability and understandability while reviewing
later.
b).
Check whether the function returns an integer
// Test getMaxLength for output integer for correct arguments
function testGetMaxLengthForCorrectReturnType() {
$paramArray = array("str1", 2);
$result = $this->obj->getMaxLength($paramArray[0], $paramArray[1]);
$this->assertTrue(is_int($result));
}
c).
Check whether is returns the length of the most lengthy string
// Test getMaxLength for Length of the lengthy string
function testGetMaxLengthForCorrectLength() {
$paramArray = array("short_string", "lenghty_string");
$result = $this->obj->getMaxLength($paramArray[0], $paramArray[1]);
$this->assertEquals(14, $result);
}
Now
the most needed test cases are implemented. From the above test
methods, some scenarios like check the function
return length of the string for arguments having only one string, is
already covered. Now just run the test and
see most of them are failing. Its time to start implement the actual
function in a
way that all te test cases are passing.
Now
again run the test. See whether all the test are passing or not. If not
your function may have some issues. Improve the function and again run
the test. Do this process until all the test are passing.
The
example we discussed was very easy one. But in real applications we
have use more complex algorithms and tactics dealing with databases. To
check the functionality of the data transaction functions, we can use a
test database with some preloaded data. Inside the setUp() function we
can create tables and load the data to the
test database. As well using the function tearDown() which automatically call after execution of any test method, we can clean the data from the database.
Sometimes
applications with MVC architecture have two or more separate layers in
Data Layer. In such kind of situation, functions need to test
independent from their layers. Lets assume in our application there are
two layers in Data Layer. One is data transaction layer which directly
deal with the database and other one is data processing layer which
process data before or after deal with database through data transaction
layer. So, while writing test for a function in data process layer
which get some data by calling a function in data transaction layer, we
should verify that the correct data set is returned from the function in
data transaction layer. To do this we can use Moc Objects.
Lets
go through one example.
* getStrings() method in DataTransaction
class return an array with two strings (No need of even declare this
class)
* getProcessedResult() method in DataProcess
class, get the array of two strings by calling the above mentioned
function (getStrings()), and return the length of the most lengthy
string out of them. This class contain getter and setter methods for use
DataTransaction class.
(Declare the getProcessedResult() function
and implement getter and setter methods)
<?php
class DataProcess {
private $dataTransaction;
function getDataTransaction() {
if(is_null($this->dataTransaction)) {
return new DataTransaction();
}
return $this->dataTransaction;
}
function setDataTransaction($dataTransaction) {
$this->dataTransaction = $dataTransaction;
}
function getProcessedResult() {
}
}
?>
* Now write the test class using the Moc Object
of DataTransaction class
<?php
require_once 'DataProcess.php';
require_once 'PHPUnit/Framework.php';
class TestMoc extends PHPUnit_Framework_TestCase {
var $dataProcess;
// This function calls before any test merhod run
function setUp() {
$this->dataProcess = new DataProcess();
}
public function testGetProcessedResult() {
// Create a Mock Object for the DataTransaction class
// mocking only the getStrings() method.
$dataTransaction = $this->getMock('DataTransaction', array('getStrings'));
// Set up the expectation for the getStrings() method
// to be called only once and with no arguments
// and return value is array("abc", "abcde")
$dataTransaction->expects($this->once())
->method('getStrings')
->with()
->will($this->returnValue(array("abc", "abcde")));
// Set the mocked DataTransaction object to
// DataProcess object using setter
$this->dataProcess->setDataTransaction($dataTransaction);
// Call the getProcessedResult() method on the $dataProcess object
// which we expect to call the mocked DataTransaction object's
// getStrings() method with no arguments.
$this->assertEquals(5, $this->dataProcess->getProcessedResult());
}
}
?>
Here, even we didn't implement (even declare) the DataTransaction class, we could run the test using Moc Object. So that we can test functions independent from the layer it contains and other needed functions.
Setting
up test suite
Test suit can be used to mange which test cases should run. We can run even all the test methods in several test cases at once. Test suite can be create as following example code snippet.
<?php
require_once 'PHPUnit/Framework.php';
class AllTests {
public static function suite() {
$suite = new PHPUnit_Framework_TestSuite("PHPUnit");
// Add needed test cases
$suite->addTestFile("MyTestClass1.php");
$suite->addTestFile("MyTestClass2.php");
return $suite;
}
}
?>
With
PHPUnit framework the percentage of the functions which covers from the unit
tests can be obtained as a coverage report. Following command can be used to obtain a coverage report. (probably you need to install xDebug : http://xdebug.org/docs/install)
$phpunit --coverage-html ./report AllTests.php
From
this report we can find out the uncovered code segments. Some of code segments in MyClass.php has not covered from the unit tests. To cover
them again test can be implemented.
So, this is a
basic idea of how to follow test driven development with PHPUnit framework. Hope this helpful to you.