<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3122725554509248157</id><updated>2011-08-13T18:06:21.222+03:00</updated><category term='video'/><category term='fun'/><category term='mysql'/><category term='php'/><category term='development'/><title type='text'>Miroslav Genev's .BLOG</title><subtitle type='html'>A(nother) repository for web development articles and snippets of various topics.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>7</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-6155067067559824725</id><published>2010-07-07T04:09:00.006+03:00</published><updated>2011-07-29T17:27:59.825+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Pivot tables in MySQL</title><content type='html'>&lt;p&gt;&lt;a href="http://mgenev.blogspot.com/2010/07/pivot-mysql.html"&gt;Прочети тази статия на български&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
In this article:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-problem-example"&gt;The problem (example)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#simple-solution"&gt;Simple solution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#automating-the-process"&gt;Automating the process&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#generating-the-sum-columns"&gt;Generating the SUM() columns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-prepared-statements-mysql"&gt;Using Prepared statements (MySQL)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-an-external-language-php"&gt;Using an external language (PHP)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;!-- INTRODUCTION --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="introduction"&gt;Introduction&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
MySQL and pivot? Try and google it. Or you already did?
&lt;/p&gt;&lt;p&gt;
I really never had the need for pivoting data, until yesterday. Then I started searching, but I didn't find a solution to enclose into a predefined function which may be reused.
&lt;/p&gt;

&lt;!-- THE PROBLEM (EXAMPLE) --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="the-problem-example"&gt;The problem (example)&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
I'm not very good at explaining (if you bothered to read other my posts you must have already noticed that), so maybe it'll be better if I start with a simple example of what we'll work with and what's the result we need. Take a look at the following table definition:
&lt;/p&gt;&lt;p&gt;
&lt;pre&gt;
CREATE TABLE `Contracts` (
    ...
    `Agent` VARCHAR(64),
    `Country` VARCHAR(3)
);
&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Amongh the other data, each row holds the name of the agent responsible for the contract, as well the country in which is the contract partner. For our example we'll need only those two columns. So, the data we're interested in looks something like that:
&lt;/p&gt;&lt;p&gt;
&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;`Agent`&lt;/th&gt;&lt;th&gt;`Country`&lt;/th&gt;&lt;th&gt;Other columns...&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;'Peter'&lt;/td&gt;&lt;td&gt;'UK'&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;'Hans'&lt;/td&gt;&lt;td&gt;'UK'&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;'Ivan'&lt;/td&gt;&lt;td&gt;'GER'&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;'Ivan'&lt;/td&gt;&lt;td&gt;'GER'&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/p&gt;&lt;p&gt;
OK, this is far from what was the data I was working with, I admit it. Also I fully realize that nobody ever, ever will have to work with data so simplified, but it's perfect to illustrate the idea for now.
&lt;/p&gt;&lt;p&gt;
So, what we need is a table with agents as columns, countries as rows, and number of contracts (agent &lt;-&gt; country) into cells, something like this:
&lt;/p&gt;&lt;p&gt;
&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;`Country`&lt;/th&gt;&lt;th&gt;`Peter`&lt;/th&gt;&lt;th&gt;`Hans`&lt;/th&gt;&lt;th&gt;`Ivan`&lt;/th&gt;&lt;th&gt;Other agents...&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;'UK'&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;'GER'&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;td&gt;(...)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/p&gt;&lt;p&gt;
The next chapter introduces a simple solution for getting results in that form.
&lt;/p&gt;

&lt;!-- SIMPLE SOLUTION --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="simple-solution"&gt;Simple solution&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
The following is a representation of the solution I found in various variants on the net. First, we need a list of all countries:
&lt;pre&gt;SELECT DISTINCT `Country` FROM `Contracts`;&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Second, we need to construct a query which selects rows from `Contracts` and groups them by agent:
&lt;pre&gt;SELECT `Agent` FROM `Contracts` GROUP BY `Agent`;&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Last we have to add the columns, each of them representing a country and displaying the sum of contracts with the agent in row. That would be done using &lt;code&gt;IF()&lt;/code&gt; and &lt;code&gt;SUM()&lt;/code&gt;:
&lt;pre&gt;
,SUM( IF( `Country`='UK', 1, 0 ) ) as `UK`
,SUM( IF( `Country`='GER', 1, 0 ) ) as `GER`
&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Then the whole select will look like this:
&lt;pre&gt;
SELECT
  `Agent`
  ,SUM( IF( `Country`='UK', 1, 0 ) ) as `UK`
  ,SUM( IF( `Country`='GER', 1, 0 ) ) as `GER`
FROM `Contracts`
GROUP BY `Agent`;
&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
How does that &lt;code&gt;SUM()&lt;/code&gt; and &lt;code&gt;IF()&lt;/code&gt; combination work? For each row (each agent), it counts the number of rows in `Contracts` with that agent, and for each row that have `Country`='UK' (when we're talking about the `UK` column) 1 is added to the cell's total.
&lt;/p&gt;&lt;p&gt;
Basically, this is the whole idea: Get the columns' names, add a &lt;code&gt;SUM()&lt;/code&gt; column for each distinct name, and include those &lt;code&gt;SUM()&lt;/code&gt;s into a select that groups rows by another (not calculated) column. One problem that arises at once - you can't reuse this select if you'd expect adding or removing countries in future.
&lt;/p&gt;

&lt;!-- AUTOMATING THE PROCESS --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="automating-the-process"&gt;Automating the process&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
So, what can be done to not have to write a select anytime the pivot table's needed? So far the only way I could think of was using two subsequent queries, the first one needed to determine the columns, and the second to get the pivot result. The second query would be generated automatically (with the first query's result included). This might be done entirely from the MySQL engine, or by using an external language.
&lt;/p&gt;

&lt;!-- GENERATING THE SUM() COLUMNS --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="generating-the-sum-columns"&gt;Generating the SUM() columns&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
One way to somewhat ease the process (and a step ahead into automating the pivot query) would be to make MySQL to generate the &lt;code&gt;SUM()&lt;/code&gt; columns:
&lt;pre&gt;
SELECT CONCAT(',SUM( IF( `Country`=\'',`Country`,'\', 1, 0 ) ) AS `',`Country`,'`')
AS `Generated_Column`
FROM `Contracts`
GROUP BY `Country`;
&lt;/pre&gt;
This produces the following output:
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;`Generated_Column`&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;,SUM( IF( `Country`='GER', 1, 0 ) ) as `GER`&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;,SUM( IF( `Country`='UK', 1, 0 ) ) as `GER`&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/p&gt;&lt;p&gt;
Once stored, this select statement can be used everytime when the pivot select is being written. However, later we'll need the result stored into a variable which is impossible unless the result is a single row. The &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat"&gt;&lt;code&gt;GROUP_CONCAT()&lt;/code&gt;&lt;/a&gt; aggregate function is what we need:
&lt;pre&gt;
SELECT GROUP_CONCAT(DISTINCT `Country` SEPARATOR ', ')
INTO @a FROM `Contracts`;
&lt;/pre&gt;
This will result in &lt;code&gt;'UK, GER'&lt;/code&gt;, stored into @a.
&lt;/p&gt;&lt;p&gt;
Now we have to add the rest of the SQL text around `Country`, but here's a problem: in our &lt;code&gt;SUM()&lt;/code&gt; definition `Country` is used twice. If we add `Country` second time in &lt;code&gt;GROUP_CONCAT&lt;/code&gt;, we cannot use &lt;code&gt;DISTINCT&lt;/code&gt; more than once (produces SQL error), but if we don't, we'll get concatenation for each row. Instead we can use a second variable to store `Country`'s value and then plate the variable where we need that value the second time:
&lt;pre&gt;
SELECT GROUP_CONCAT(DISTINCT @b=`Country`, ' ', @b SEPARATOR ', ')
INTO @a FROM `Contracts`;
&lt;/pre&gt;
Now &lt;code&gt;@a = 'UK UK, GER GER'&lt;/code&gt;. Let's add the rest of the text:
&lt;pre&gt;
SELECT GROUP_CONCAT(
    DISTINCT 'SUM( IF( `Country`=\'', @b = `Country`,
    '\', 1, 0 ) ) AS `', @b, '`' SEPARATOR ', ')
INTO @a from `Contracts`;
&lt;/pre&gt;
The result is &lt;code&gt;SUM( IF( `Country`='0', 1, 0 ) ) AS `GER`, SUM( IF( `Country`='1', 1, 0 ) ) AS `GER`&lt;/code&gt;.
&lt;/p&gt;

&lt;!-- USING PREPARED STATEMENTS (MYSQL) --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="using-prepared-statements-mysql"&gt;Using Prepared statements (MySQL)&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;This is my solution:
&lt;pre&gt;
SELECT GROUP_CONCAT(DISTINCT 'SUM( IF( `Country`=\'', @b = `Country`,'\', 1, 0 ) ) AS `', @b, '`' SEPARATOR ', ')
INTO @a from `Contracts`;
SET @a = CONCAT('SELECT `Agent`, ', @a, 'FROM `Contracts` GROUP BY `Agent`');
PREPARE s FROM @a;
EXECUTE s;
DEALLOCATE PREPARE s;
&lt;/pre&gt;
Following is a step-by-step explanation.
&lt;/p&gt;&lt;p&gt;
The only way to evaluate SQL statements from strings I know of is by using &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-prepared-statements.html"&gt;prepared statements&lt;/a&gt;. This is the simplified synthax:
&lt;pre&gt;
PREPARE s FROM 'SELECT * FROM `Contracts`';
EXECUTE s;
DEALLOCATE PREPARE s;
&lt;/pre&gt;
The main purpose of prepared statements isn't exactly the ability to evaluate dynamically created SQL statements and that's the reason I'm using this feature in it's most plain variant.
&lt;/p&gt;&lt;p&gt;
Now that @a holds the &lt;code&gt;SUM&lt;/code&gt; columns definitions (see &lt;a href="#generating-the-sum-columns"&gt;"Generating the SUM() columns"&lt;/a&gt;") we can build the pivot &lt;code&gt;SELECT&lt;/code&gt;:
&lt;pre&gt;
SET @a = CONCAT('SELECT `Agent`, ', @a, 'FROM `Contracts` GROUP BY `Agent`');
&lt;/pre&gt;
And the rest is easy:
&lt;pre&gt;
PREPARE s FROM @a;
EXECUTE s;
DEALLOCATE PREPARE s;
&lt;/pre&gt;
&lt;/p&gt;

&lt;!-- USING AN EXTERNAL LANGUAGE (PHP) --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="using-an-external-language-php"&gt;Using an external language (PHP)&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
Here's the solution:
&lt;pre&gt;
$link = mysql_connect('SQL host', 'username', 'password');
$query = 'SELECT DISTINCT `Country` FROM `Contracts`;';
$result = mysql_query($query, $link);
$countries = '';
while ($row = mysql_result($result))
  $countries .= ",SUM( IF( `Country`='{$row[0]}', 1, 0 ) ) as `{$row[0]}` ";
mysql_free_result($result);
$query = 'SELECT `Agent`, ' . $countries . 'FROM `Contracts` GROUP BY `Agent`';
$result = mysql_query($query, $link);
&lt;/pre&gt;
Read below for a step-by-step explanation.
&lt;p&gt;
This method involves two requests to the MySQL server - one to get the country names, and the second to query the pivot table. Although any language can be used this example implements PHP.
&lt;/p&gt;&lt;p&gt;
First of all establish a MySQL connection:
&lt;pre&gt;$link = mysql_connect('SQL host', 'username', 'password');&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Then get the countries:
&lt;pre&gt;
$query = 'SELECT DISTINCT `Country` FROM `Contracts`;';
$result = mysql_query($query, $link);
$countries = '';
while ($row = mysql_result($result))
  $countries .= ",SUM( IF( `Country`='{$row[0]}', 1, 0 ) ) as `{$row[0]}` ";
mysql_free_result($result);
&lt;/pre&gt;
Instead of getting rows containing the plain country names we may take advantage of the previously reviewed methods to generate the columns definitions (see &lt;a href="#generating-the-sum-columns"&gt;"Generating the SUM() columns"&lt;/a&gt;):
&lt;pre&gt;
$query = "SELECT CONCAT(',SUM( IF( `Country`=\\'',`Country`,'\\', 1, 0 ) ) AS `',`Country`,'`') AS `Generated_Column` FROM `Contracts` GROUP BY `Country`;";
$result = mysql_query($query, $link);
$countries = '';
while ($row = mysql_result($result)) {
  $countries .= $countries;
}
&lt;/pre&gt;
...or even:
&lt;pre&gt;
$query = "SELECT GROUP_CONCAT(DISTINCT 'SUM( IF( `Country`=\'', @b = `Country`,'\', 1, 0 ) ) AS `', @b, '`' SEPARATOR ', ') INTO @a from `Contracts`;";
$result = mysql_query($query, $link);
$row = mysql_result($result);
$countries = $row[0];
&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
Now we need to build and send the main query, using the already prepared columns for countries:
&lt;pre&gt;
$query = 'SELECT `Agent`, ' . $countries . 'FROM `Contracts` GROUP BY `Agent`';
$result = mysql_query($query, $link);
&lt;/pre&gt;
And we can display, save into file or do whatever we want with the result.
&lt;/p&gt;&lt;p&gt;
Do you have another solutions or questions? Please comment.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-6155067067559824725?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/6155067067559824725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/07/pivot-tables-in-mysql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/6155067067559824725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/6155067067559824725'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/07/pivot-tables-in-mysql.html' title='Pivot tables in MySQL'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-2947998688221236890</id><published>2010-07-06T14:14:00.012+03:00</published><updated>2011-07-29T17:28:14.218+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>JavaZone trailer: JAVA 4-ever</title><content type='html'>&lt;p&gt;&lt;a href="http://mgenev.blogspot.com/2010/07/javazone-java-4-ever.html"&gt;Българска версия на този пост&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hilarous video.&lt;/p&gt;

&lt;object width="499" height="306"&gt;&lt;param name="movie" value="http://www.youtube.com/v/KrfpnbGXL70&amp;amp;hl=en_GB&amp;amp;fs=1?rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/KrfpnbGXL70&amp;amp;hl=en_GB&amp;amp;fs=1?rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="499" height="306"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-2947998688221236890?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/2947998688221236890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/07/javazone-trailer-java-4-ever.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/2947998688221236890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/2947998688221236890'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/07/javazone-trailer-java-4-ever.html' title='JavaZone trailer: JAVA 4-ever'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-9118508359324377228</id><published>2010-07-04T22:39:00.006+03:00</published><updated>2011-07-29T17:28:28.259+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Config.ini vs. config.php - Combining both</title><content type='html'>&lt;p&gt;&lt;a href="http://mgenev.blogspot.com/2010/07/configini-configphp.html"&gt;Прочети тази статия на български&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
Not long ago I wrote an article about using INI files as settings containers for PHP scripts (you can read it here - &lt;a href="http://mgenev.blogspot.com/2010/06/configuration-ini-vs-constants.html"&gt;Config.ini vs. config.php&lt;/a&gt;).
&lt;/p&gt;&lt;p&gt;
I wrote a small PHP module which takes an INI file and produces a PHP file, containing all the INI parameters and values as constants definitions.
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;/p&gt;
The module is really easy to set up and work with so if you need a quick way to add settings to your script without having to search PHP files for lines such as &lt;code&gt;$database = 'not_anymore';&lt;/code&gt; or &lt;code&gt;define('DEFAULT_DIRECTORY', './res');&lt;/code&gt; everytime you need to check or to change options, you can use my (proudly presented) &lt;em&gt;PHP-INI configuration parser&lt;/em&gt; (its name happened to be a little longer than the PHP code it consists of). You can download it as a RAR archive from this link: &lt;a href="http://mgenev.stonybg.com/config-0.1b/config-0.1.rar"&gt;Config 0.1beta&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
As you can see, it's only version 0.1 and still beta (which will stay like this until at least one or two readers actually stop by this blog, happen to read this article, and on top of that decide to download and try my piece of code. And to post a comment with their opinion, maybe? Pretty please?), but I will see to further improvements, adding new features and so on. When and if I have the time for it.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-9118508359324377228?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/9118508359324377228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/06/configini-vs-configphp-combining-both.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9118508359324377228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9118508359324377228'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/06/configini-vs-configphp-combining-both.html' title='Config.ini vs. config.php - Combining both'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-9216678822434019635</id><published>2010-06-30T21:45:00.020+03:00</published><updated>2011-07-29T17:28:40.003+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Exceptions in PHP</title><content type='html'>&lt;p&gt;&lt;a href="http://mgenev.blogspot.com/2010/06/exceptions-php.html"&gt;Прочети тази статия на български&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
In this article:
&lt;ul&gt;&lt;li&gt;&lt;a href="#exceptions"&gt;Exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the_exception_object"&gt;The Exception object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#exception_subclasses"&gt;Exception subclasses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#why_wont_this_catch_the_exception"&gt;Why won't this catch the exception?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;!-- EXCEPTIONS --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="exceptions"&gt;Exceptions&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
&lt;a href="http://php.net/manual/en/language.exceptions.php"&gt;Exceptions&lt;/a&gt; and &lt;code&gt;try-catch&lt;/code&gt; blocks are introduced in PHP version 5.
&lt;/p&gt;&lt;p&gt;
Here's an example for the syntax of &lt;code&gt;try-catch&lt;/code&gt;:
&lt;pre&gt;
try {
    ...
} catch (Exception $e) {
    ...
}
&lt;/pre&gt;
First, statements inside the &lt;code&gt;try&lt;/code&gt; block are executed. If an exception is thrown PHP ignores next statements until a proper &lt;code&gt;catch&lt;/code&gt; block is found, in which case the &lt;code&gt;catch&lt;/code&gt; block is executed.
&lt;/p&gt;&lt;p&gt;
An interesting thing about ignoring next statements (until the next &lt;code&gt;catch&lt;/code&gt; block) is that even statements &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;try&lt;/code&gt; block will be ignored since &lt;code&gt;catch&lt;/code&gt; has not been found (and executed) yet.
&lt;/p&gt;&lt;p&gt;
If there's no matching &lt;code&gt;catch&lt;/code&gt; block until the end of the script a Fatal error occurs.
&lt;/p&gt;&lt;p&gt;
If there's no exception thrown, the whole &lt;code&gt;try&lt;/code&gt; block is executed, and the &lt;code&gt;catch&lt;/code&gt; one is ignored.
&lt;/p&gt;&lt;p&gt;
Consider the following example:
&lt;pre&gt;
function display_text($text) {
    if (!$text)
        throw new Exception('Nothing to display');
    else
        echo $text;
}

try {
    display_text('First line.');
    display_text(); // An exception is thrown, so the block execution breaks here
    display_text('This will be ignored.');
}
// Any statements here would be ignored too - the exception must be caught first
catch (Exception $e) {
    echo 'Exception: ' . $e-&amp;gt;getMessage() . "\n";
}
// script continues...
&lt;/pre&gt;
PHP allows catching exceptions from nested levels - in previous example the function we call throws the exception, but instead it could call another function (which calls another function, and so on) which throws the exception. We'll be able to catch that exception too.&lt;br /&gt;
&lt;/p&gt;

&lt;!-- THE EXCEPTION OBJECT --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="the_exception_object"&gt;The Exception object&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
As seen from the afore example, exceptions are thrown by creating new object and assigning a message to it (&lt;code&gt;throw new Exception()&lt;/code&gt;). Later in the script we're able to catch it with &lt;code&gt;catch (Exception $obj_name)&lt;/code&gt;. Inside the &lt;code&gt;catch&lt;/code&gt; block we may use the object's methods to get information about the exception.
&lt;/p&gt;&lt;p&gt;
Here's a short reference to (most of) the Exception class methods (check the &lt;a href="http://php.net/manual/en/class.exception.php"&gt;PHP manual on the Exception class&lt;/a&gt; for more information):
&lt;pre&gt;
Exception::__construct ( [ string $message = '' [, int $code = 0 [, Exception $previous = null ] ] ] )
&lt;/pre&gt;
Constructs the exception object (i.e. &lt;code&gt;throw new Exception(...)&lt;/code&gt;). $message is the error (exception) message, $code is an optional error parameter, by which an error number might get defined. $previous can be used to link the exception to a previous one (for example, you may have a case when catching one exception results in more exceptions - link to previous exceptions can be linked so at the end all messages could be proceeded).
&lt;pre&gt;
string    Exception::getMessage()
int       Exception::getCode()
Exception Exception::getPrevious()
&lt;/pre&gt;
Returns the mentioned before message, code, and link to previous exception.
&lt;pre&gt;
string    Exception::getFile()
&lt;/pre&gt;
Returns the filename of the PHP where the exception was thrown.
&lt;pre&gt;
int       Exception::getLine()
&lt;/pre&gt;
Returns the line number in the PHP file where the exception occured.
&lt;pre&gt;
array     Exception::getTrace()
string    Exception::getTraceAsString()
&lt;/pre&gt;
These two methods will return more detailed information such as function name where the exception occured, arguments the function has been called with (filename and line number as provided as well). The difference is that while &lt;code&gt;getTrace()&lt;/code&gt; does provide the information as an associated array (['Line']=&amp;gt;23, ['Function']=&amp;gt;'test()' etc.), &lt;code&gt;getTraceAsString()&lt;/code&gt; formats that information in one string.
&lt;/p&gt;&lt;p&gt;
Short example:&lt;br /&gt;
&lt;pre&gt;
...
catch (Exception $e) {
    echo 'Error: '.$e-&amp;gt;getMessage().' in "'.$e-&amp;gt;getFile().'" at line '.$e-&amp;gt;getLine();
}
&lt;/pre&gt;
&lt;/p&gt;

&lt;!-- EXCEPTION SUBCLASSES --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="exception_subclasses"&gt;Exception subclasses&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
&lt;pre&gt;
class MyException extends Exception {}
class MyOtherException extends Exception {}

function test($var) {
    if ($var &amp;gt; 0) throw new MyException('Positive number');
    if ($var &amp;lt; 0) throw new MyOtherException('Negative number');
    return 0;
}

try { echo test(1); }
catch (MyException $e) { echo 'The number was greater than zero.'; }
catch (MyOtherException $e) { echo 'The number was below zero.'; }
&lt;/pre&gt;
Subclasses of Exception can be defined (in fact, many predefined exception classes exist in PHP), then you may catch different descendants with different &lt;code&gt;catch&lt;/code&gt; blocks. Since &lt;code&gt;MyException&lt;/code&gt; and &lt;code&gt;MyOtherException&lt;/code&gt; are child classes of &lt;code&gt;Exception&lt;/code&gt;, instead of two &lt;code&gt;catch(My... $e)&lt;/code&gt; we could use one &lt;code&gt;catch(Exception $e)&lt;/code&gt; statement and still the exception will be properly caught.  One method of dealing with different exceptions would be using many subclasses, and other method could make use of the &lt;code&gt;$code&lt;/code&gt; parameter to define the type of error.
&lt;/p&gt;

&lt;!-- WHY WON'T THIS CATCH THE EXCEPTION? --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="why_wont_this_catch_the_exception"&gt;Why won't this catch the exception?&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
&lt;pre&gt;
function read_first_line($filename) {
    try {
        $a = file($filename);
    } catch(Exception $e) {
        return false;
    }
    return $a[0];
}

echo read_first_line('myfile.txt');
&lt;/pre&gt;
This won't work. Still your page will yell something like &lt;em&gt;'Error: could not open stream...'&lt;/em&gt; in case of error instead of &lt;em&gt;'false'&lt;/em&gt;. This is because old PHP functions still don't take advantage of the Exceptions mechanism. The "old" error handling method is used instead but I'll write about that in another article.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-9216678822434019635?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/9216678822434019635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/06/exceptions-in-php.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9216678822434019635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9216678822434019635'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/06/exceptions-in-php.html' title='Exceptions in PHP'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-9114655686335624119</id><published>2010-06-24T14:19:00.009+03:00</published><updated>2011-07-29T17:28:53.531+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>IT Helpdesk (Video)</title><content type='html'>&lt;p&gt;
&lt;a href="http://mgenev.blogspot.com/2010/07/helpdesk.html"&gt;Българска версия на този пост&lt;/a&gt;
&lt;/p&gt;

&lt;object width="445" height="364"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Be3alRoxkOo&amp;hl=en_GB&amp;fs=1&amp;rel=0&amp;border=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Be3alRoxkOo&amp;hl=en_GB&amp;fs=1&amp;rel=0&amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="445" height="364"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;
&lt;br /&gt;
Do you know of more such videos? Comment and share!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-9114655686335624119?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/9114655686335624119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/06/it-helpdesk-video.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9114655686335624119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/9114655686335624119'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/06/it-helpdesk-video.html' title='IT Helpdesk (Video)'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-3524715721799704159</id><published>2010-06-24T01:21:00.012+03:00</published><updated>2011-07-29T17:29:09.347+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Counting the number of weeks in given period (MySQL)</title><content type='html'>&lt;p&gt;
&lt;a href="http://mgenev.blogspot.com/2010/06/mysql.html"&gt;Прочети статията на български&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Few days ago a friend of mine told me that he need to count number of weeks in given period. Simple division by 7 of the number of days wasn't what he needed - the weeks had to be counted as number of Monday-Sunday fragments (in addition to any incomplete weeks at the start or the end of the period). I wrote a simple stored function to do the task:
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
&lt;pre&gt;
`week_count`
  (
    StartDate DATETIME,
    EndDate DATETIME,
    CountIncompleteWeeks
  )
  RETURNS INT
&lt;/pre&gt;
&lt;code&gt;StartDate&lt;/code&gt; and &lt;code&gt;EndDate&lt;/code&gt; define the period to count weeks from. &lt;code&gt;CountIncompleteWeeks&lt;/code&gt;, determines if incomplete weeks have to be counted (&lt;code&gt;CountIncompleteWeeks=1&lt;/code&gt;) or discarded (&lt;code&gt;CountIncompleteWeeks=0&lt;/code&gt;).
&lt;/p&gt;&lt;p&gt;
The function returns number of weeks (minimum of 1) or -1 in case of error (if &lt;code&gt;StartDate&lt;/code&gt; or &lt;code&gt;EndDate&lt;/code&gt; aren't of proper &lt;code&gt;DATETIME&lt;/code&gt; format, or if &lt;code&gt;StartDate&lt;/code&gt; is bigger than &lt;code&gt;EndDate&lt;/code&gt;).
&lt;/p&gt;&lt;p&gt;
&lt;em&gt;NOTE&lt;/em&gt;: Both dates count towards the number of days (so if both date values are equal, that means an interval of 1 day, resp. the result will be &lt;code&gt;1&lt;/code&gt; week).
&lt;/p&gt;&lt;p&gt;
&lt;pre&gt;
CREATE FUNCTION `week_count`
   (
      StartDate DATETIME,
      EndDate DATETIME,
      CountIncompleteWeeks INT
   )
   RETURNS INT
   DETERMINISTIC
   BEGIN
      DECLARE Days, Weeks, WDay INT;
      SET Weeks = 0;
      /* Get the number of days in the given period */
      SET Days = IFNULL(DATEDIFF(EndDate, StartDate) + 1, -1);
      IF Days &lt; 1 THEN RETURN -1; END IF;

      /* If the start date isn't Monday, subtract this incomplete week from the
         total days number (and count as 1 week if PlusIncomplete &gt; 0) */
      SET WDay = IF(DAYOFWEEK(StartDate) = 1, 7, DAYOFWEEK(StartDate) - 1);
      IF WDay &gt; 1 THEN
         IF CountIncompleteWeeks &gt; 0 THEN SET Weeks = 1; END IF;
         SET Days = Days - (8 - WDay);
         -- If after subtracting no days have left, return 1.
         IF Days &lt; 1 THEN RETURN Weeks; END IF;
      END IF;

      /* If the end date isn't Sunday, subtract this incomplete week from the
         total days number (and count as 1 week if PlusIncomplete &gt; 0) */
      SET WDay = if(DAYOFWEEK(EndDate) = 1, 7, DAYOFWEEK(EndDate) - 1);
      IF WDay &lt; 7
      THEN
         IF CountIncompleteWeeks &gt; 0 THEN SET Weeks = Weeks + 1;
         SET Days = Days - WDay;

         /* If after subtracting there are no more days, return the number of weeks
            counted so far */
         IF Days &lt; 1 THEN RETURN Weeks; END IF;
      END IF;

      /* Divide the remaining days to weeks, add any incomplete one,
         then return the result. */
      RETURN Weeks + (Days / 7);
   END;
&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;
I'd welcome any comments (if someone ever reads this post, of course) about this function. Please feel free to criticize or to propose better variants.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-3524715721799704159?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/3524715721799704159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/06/count-number-of-weeks-in-given-period.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/3524715721799704159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/3524715721799704159'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/06/count-number-of-weeks-in-given-period.html' title='Counting the number of weeks in given period (MySQL)'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3122725554509248157.post-6070244627512881227</id><published>2010-06-23T15:34:00.017+03:00</published><updated>2011-07-29T17:29:21.669+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Config.ini vs. config.php</title><content type='html'>&lt;p&gt;
&lt;a href="http://mgenev.blogspot.com/2010/06/configini-configphp.html"&gt;Прочети статията на български&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Recently, I was thinking - what is the best practice to load previously stored settings to your PHP script? One way is to have a PHP file containing &lt;code&gt;define()&lt;/code&gt; statements. Then you need to &lt;code&gt;include()&lt;/code&gt; the file in your main script and you have access to the options through constants.
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
Another way is to keep an INI file, and load it by using &lt;code&gt;parse_ini_file()&lt;/code&gt; or &lt;code&gt;parse_ini_string()&lt;/code&gt; functions. Of course, there are many other possible approaches, but for the most cases using the INI-file or constants methods are the most appropriate ones. So, in short: which is better - the INI file or the constants?
&lt;/p&gt;

&lt;!-- BENEFITS AND DRAWBACKS --&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;&lt;a name="benefits_and_drawbacks"&gt;Benefits and drawbacks&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
One thing to consider is if it's about a script that would be used and maintained not only by its creators - a module or even a whole application. It's way more convenient for a user to modify settings in an INI file rather than a PHP one (strictly speaking, having to edit a PHP file is having to modify the source code, which isn't the best way to propose an "interface" to application settings).
&lt;/p&gt;&lt;p&gt;
Also, using INI files grants you the ability to divide your settings into sections (although this benefit is doubtful, when you can easily just add "SECTIONNAME_" to the identifiers of your constants, and if you really need a tree-style of variables you should consider using XML instead).
&lt;/p&gt;&lt;p&gt;
On the other hand, parsing INI files using core PHP functions isn't very useful: first of all, you end up with an array of strings (no int or float, not to mention another data types). And, which is of bigger significance, you just can't have any characters you wish in your values, regardless of using &lt;code&gt;INI_SCANNER_NORMAL&lt;/code&gt; or &lt;code&gt;INI_SCANNER_RAW&lt;/code&gt; in the &lt;code&gt;parse_ini_file/string&lt;/code&gt; function. (This particular drawback could be avoided by supplying custom functions to parse INI data.)
&lt;/p&gt;&lt;p&gt;
And the last, but maybe the most important thing, is the speed. When speaking about a site with heavy traffic, thus frequently loading the settings into the script, the speed is of great importance. I have made the following test:&lt;br /&gt;
I generated 100 &lt;code&gt;.ini&lt;/code&gt; files containing 100 key-value pairs each. I also generated 100 &lt;code&gt;.php&lt;/code&gt; files with 100 constant definitions each. Then I executed 10 times in a row a script that loads all the files and measures the time needed to load them (of course, separate measurement for loading the &lt;code&gt;.ini&lt;/code&gt;-s and for the constants). Here are the results on my machine (CPU: Sempron 3100+ @1.8 GHz, RAM: 768 MB DDR400; WAMP):
&lt;pre&gt;
Seconds to load 10000 values:
Script exec.  INI        Constants
------------  ---------  ---------
1-st time     0.0709*    0.0107*
2-nd          0.0181     0.0031
3-rd          0.0183     0.0023
4-th          0.0183     0.0029
5-th          0.0187     0.0029
6-th          0.0179     0.0021
7-th          0.0182     0.0027
8-th          0.0187     0.0030
9-th          0.0184     0.0022
10-th         0.0188     0.0035

* It's funny to observe how caching of files affects performance
&lt;/pre&gt;
The conclusioin? Using include() to your script and loading configuration values as constants is &lt;em&gt;MUCH&lt;/em&gt; faster than loading them from INI file.
&lt;/p&gt;

&lt;!-- A SHORT RESUME --&gt;
&lt;/p&gt;&amp;nbsp;&lt;p&gt;&lt;h4&gt;&lt;a name="a_short_resume"&gt;A short resume&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;
&lt;ul&gt;&lt;li&gt;Editing configuration stored in INI format is more convenient than editing PHP file.&lt;/li&gt;
&lt;li&gt;Defining constants by including PHP file into the main script is more safer and flexible in comparison to the INI-load method (unless using custom INI parsing functions).&lt;/li&gt;
&lt;li&gt;Defining constants is several times faster than loading INI values.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3122725554509248157-6070244627512881227?l=mgenev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mgenev.blogspot.com/feeds/6070244627512881227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://mgenev.blogspot.com/2010/06/configuration-ini-vs-constants.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/6070244627512881227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3122725554509248157/posts/default/6070244627512881227'/><link rel='alternate' type='text/html' href='http://mgenev.blogspot.com/2010/06/configuration-ini-vs-constants.html' title='Config.ini vs. config.php'/><author><name>Miroslav Genev</name><uri>http://www.blogger.com/profile/03941341381501923160</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-6KNXwfIwxk4/TkaS52IdI9I/AAAAAAAAAE0/vcmypUIHMgQ/s1600/MG.JPG'/></author><thr:total>2</thr:total></entry></feed>
