Current Projects: Custom PHP/MySQL Members CMS
It’s been a long time since I actually coded a custom standalone php application for a client and I found my skills to be a little rusty at first. The reason I don’t get to code a lot of custom applications anymore is because most of the client projects we see today are built on top of a cms like wordpress or a framework like modx. In this instance the client already had their website developed internally and just needed us to develop an enhanced members area so they could communicate and share information with their members.
The membership table for this project has approximately 500 members in it, each member has the ability to authenticate by login and then view protected content. Members can also submit changes to their contact or login information that has to be approved before it’s accepted. Of course the normal features such as lost password restoration are also in place.

This solution was built entirely using PHP and all of the data is stored in 17 tables inside a MySQL database. For easier management of a large membership database with over 500 members, I also built in the ability to export and import data to the database from either XLS or CSV format. This should be another convenient aspect of the solution for our client.
I am pretty anxious to demo this application and bug-test it this next week. I used the newest version of PHPRunner IDE to develop this project and even though I was a little out of practice I still was able to code the project a lot faster than if I had used another tool like Eclipse or Notepad. The new version of PHPR is very nice, it’s been a while since I upgraded and have to admit that there are so many new features that I haven’t even looked into yet.
Larry Taylor on UML (#RefresCA)
Normally after our Central Arkansas Refresh meetings I come home, have a late dinner with my wife and then blog about the meeting but this past Tuesday night I think I had brain overload. Larry Taylor from the Software Designers gave a presentation on UML (unified markup language). I went into the meeting with a little bit of knowledge regarding UML and what role it plays in software development but haven’t actually used it enough to become very familiar with it. I had a general idea as to what the “open diamonds” and “closed diamonds” meant but that’s about as far as it went for me.
Larry gave an awesome explanation as to how he uses UML to scope out just about every project he works on and demonstrated it’s value on a workflow. After seeing this I had to ask myself if I was leaving out a key process right after discovery on custom applications that I do. Granted, I don’t do a whole lot of custom applications (probably 10 all of last year), and all of the applications that I build are pretty intermediate in architecture, but he made this look so impressive I got thinking about my processes pretty hard.

This is a screengrab from NetBeans IDE demonstrating UML
-
Since all of my application development is done in PHP, and all of my PHP is generated inside PHPRunner IDE, I don’t think that I can personally benefit from adding formal UML to my list of processes because PHPR more or less is built around the idea of being extremely visual and easy to read. Granted, if I was coding something in a language that I am not as familiar with, or if the project was simply enormous, I can see where UML could really simplify things.
Another advantage that I can see for UML is the ability to spread things out evenly to a team of developers that each might be delivering a specific component of the project. Larry even referenced the Microsoft Notecard process where each developer on a team would receive a notecard explaining the component that there were supposed to deliver.
If you develop any type of applications and aren’t using UML, you should at least check it out and see if it is something you or your team could benefit from using. Also, if you were unable to attend this past weeks #RefreshCA meeting, you can get Larry’s slides from the presentation on his blog: UML Presentation. Larry also has additional resources on his blog about UML that can be found here…
Larry has been coding for almost twenty years and heads up the Little Rock .NET users group, if you live in the Central Arkansas area and work with .NET you should check them out. If you aren’t for sure what I am talking about when I say UML, here’s an image that will give you a basic idea.
Locking Down Authentication Inside PHPRunner
One of the biggest challenges you face when building hosted applications is how to prevent brute force or guessed password authentications. Especially given the number of warez type applications that are out there that allow unsavory users to do just that. Well, I found a resource on Xlinesoft’s website that demonstrates how to do block a user after three unsuccessful attempts to login to your application.
This schema uses visitors IP address to store log attempts in the database and block access to to the login feature for 30 minutes after the third unsuccessful attempt. This schema involves Events function which is available in ASPRunnerpro 6.0/PHPRunner 5.0, I have reposted the processes involved for PHPRunner below, but you can find the ASPRunner notes here…
Step One:
In MySQL Server run the following script to create table in your database that logs login attempts. The box below demonstrates the MySQL command.
1: CREATE TABLE `LoginAttempts`
2: (
3: `IP` VARCHAR(20) NOT NULL,
4: `Attempts` INT NOT NULL,
5: `LastLogin` DATETIME NOT NULL
6: )
Step Two:
Open your PHPRunner project and go to the security tab and switch on the “Create Login Page” checklist.
Check the Username and password from database option and choose appropriate fields. If you have no table in which all of the login details are stored you have to create it.
Step Three:
Add three global events on the Events tab: BeforeLogin, AfterSuccessfulLogin, AfterUnsuccessfulLogin. Below you will find the PHPRunner example for this:
1: <?
2: function BeforeLogin($username, $password)
3: {
4: //********** Custom code ************
5: // check if this IP address is currently blocked
6: global $conn;
7: $sql = "select Attempts, LastLogin from LoginAttempts where ip = '" . $_SERVER["REMOTE_ADDR"] . "'";
8: $rs = db_query($sql,$conn);
9: $data = db_fetch_array($rs);
10:
11: if (!$data || !strlen($data["LastLogin"]))
12: return true;
13:
14: $atime = db2time($data["LastLogin"]);
15: $time = mktime($atime[3],$atime[4],$atime[5],$atime[1],$atime[2],$atime[0]);
16: $diff = (time()-$time)/60;
17:
18: if ($data["Attempts"]>=3)
19: {
20: if($diff<30)
21: {
22: echo "<p align=center><br><font color=red><b>Access denied for 30 minutes</b> <font></p>";
23: return false;
24: }
25: else
26: {
27: db_exec("update LoginAttempts set Attempts=0 where ip = '" . $_SERVER["REMOTE_ADDR"] . "'",$conn);
28: return true;
29: }
30: }
31: return true;
32: }
33:
34: function AfterSuccessfulLogin()
35: {
36: //********** Custom code ************
37: // clear previous attempts
38:
39: global $conn;
40: db_exec("update LoginAttempts set Attempts=0 where ip = '" . $_SERVER["REMOTE_ADDR"] . "'",$conn);
41:
42: }
43:
44: function AfterUnsuccessfulLogin()
45: //********** Custom code ************
46: // increase number of attempts
47: // set last login attempt timeif required
48: {
49: global $conn;
50: $sql = "select * from LoginAttempts where ip = '" . $_SERVER["REMOTE_ADDR"] . "'";
51: $rs = db_query($sql,$conn);
52: $data = db_fetch_array($rs);
53:
54: if($data)
55: {
56: $attempts = $data["Attempts"]+1;
57:
58: if($attempts==3)
59: db_exec("update LoginAttempts set Attempts=" . $attempts . ", LastLogin=now() where ip = '" .$_SERVER["REMOTE_ADDR"] . "'",$conn);
60: else
61: db_exec("update LoginAttempts set Attempts=" . $attempts . " where ip = '" .$_SERVER["REMOTE_ADDR"] . "'",$conn);
62: }
63: else
64: db_exec("insert into LoginAttempts (Attempts,IP,LastLogin) values (1, '".$_SERVER["REMOTE_ADDR"] . "',NOW())",$conn);
65: }
66: ?>
Step Four:
You should finish the code generation / compiling process and upload your application. It’s important to remember that by doing this, your visitors have to enter their username and password to gain access to the site. After the third unsuccessful login attempt, their IP addresses access will be denied for 30 minutes. When the visitor tries to login when the account is blocked they will see message saying access is denied.
Find out how to do this for ASPRunner also…
——————————————————————
There are a lot of other useful resources outlined for PHPRunner users in the Articles section on Xlinesoft’s website, you can find them here…
Facebook | PHPRunner User Group
I recently started a PHPRunner user group on Facebook in hopes of connecting with other PHPR programmers. So far we have 6 members, myself and of course Sergey, who is the CEO of the company that puts out PHPRunner, and four other guys. My main goal for creating the group was to provide some awareness of PHPRunner as well as being a way for PHPR developers to reach out to one another for paid assistance on projects built with PHPR. Honestly there have been times that I would have hired a developer to assist me on projects if he was familiar with PHPR.
Of course the formation of the group was met with a little opposition by users on the support forums because they didn’t see the need for such a group because the forums on xlinesoft’s website are already pretty active. I pointed out that there are user groups on Facebook for other IDE software clients such as CodeSmith, CodeCharge, and Eclipse and having a group for PHPR would probable help bring exposure to the product.
I also have always felt funny about soliciting developers from message boards, not to mention it’s forbidden on a lot of support forums, but having a Facebook group is perfect for something like this. I have made it very clear that the user group is not a support forum and is only intended for developers to network with one another, showcase their work, and to post projects for bid. I am hopeful that the group will grow in size and we can develop a pretty good community but I am also a realist and I know that PHPR users make up a pretty small piece of the pie. As I am sure a lot of you that have talked with me about PHPR already know, I am pretty hung up on this code generation thing and out of all the products I have seen on the market, PHPRunner is the best one out there.
If you are looking to learn more about PHPRunner, be sure to checkout their website.
AJAX for updating other fields
Here is a cool trick for utilizing AJAX to update other fields on pages inside of PHPR code. I have to thank Giles for pointing out calculation.js to me, see his notes below in this post.
The problem I wanted to solve was updating many price fields on a page as the user entered data in other fields using pricing information in a table not displayed at the time. The problem could be solved with a beforerecordupdated event but that would mean the user would have to save the record each time they wanted to see the price with subsequent impact on performance and usability.
The approach was to use AJAX responseXML. A javascript "calculation.js" gets the pricing data from "getdataxml.php", then performs the calc and displays the data on the user’s current page. The price data is only retrieved on the first execution of the script as it persists with the page. The script is executed whenever the fields involved in the calc are changed by the user.
calculation.js
1: var xmlHttp
2: var gotdata //used as flag so data only retreived once for each page call up
3:
4: // declare a var for each data element being read from the mysql table by the getdataxml.php page
5: // declared here so they persist for the life of the page
6: var id
7: var date_created
8: var date_modified
9: var pricefield
10:
11: //main function
12: //on first activation it sets up to get the data from mysql
13: //on subsequent activations it does the calculation...
14:
15: function docalcofsomesort(str)
16: {
17: xmlHttp=GetXmlHttpObject()
18: if (xmlHttp==null)
19: {
20: alert ("Browser does not support HTTP Request")
21: return
22: }
23: if (gotdata==null) //go get the data if not already done so...
24: {
25: alert ("Refreshing data") //just a footprint for debugging purposes
26: var url="getdataxml.php"
27: url=url+"?q="+str
28: url=url+"&sid="+Math.random() //the random element is attached to stop page caching on the server
29: xmlHttp.onreadystatechange=stateChanged
30: xmlHttp.open("GET",url,true)
31: xmlHttp.send(null) // ok, let's send the request and then wait for an answer...
32: }
33: else //if data already there do the calc...
34: {
35: alert ("Calculating") //just a footprint for debugging purposes
36: var newvalue = updatevalue()
37: document.getElementById("value_fieldwhereyouwantresult").value= newvalue; //store the result on the page...
38: }
39: }
40:
41: function stateChanged() //answer is back, let's grab the data
42: {
43:
44: if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
45: {
46: xmlDoc=xmlHttp.responseXML;
47:
48: alert ("Storing data to variables") //just a footprint for debugging purposes
49: try
50: {
51: id = xmlDoc.getElementsByTagName("id")[0].childNodes[0].nodeValue;
52: date_created = xmlDoc.getElementsByTagName("date_created")[0].childNodes[0].nodeValue;
53: date_modified = xmlDoc.getElementsByTagName("date_modified")[0].childNodes[0].nodeValue;
54: anotherfield = xmlDoc.getElementsByTagName("pricefield")[0].childNodes[0].nodeValue;
55:
56: gotdata = 1;
57: alert("Stored data") //just a footprint for debugging purposes
58: docalcofsomesort("1") //recall the function to actually do the calc after getting the data
59: }
60: catch(err)
61: {
62: // with xml used in this way it's hard to know exactly what's going on as you can't directly
63: // see the returned data...the try-catch is an attempt to help trap missing data.
64: // could do with better error handling as it only helps a tiny bit. can get this alert for other
65: // reasons
66:
67: alert ("A value in the data has not been set up. Correct this and try again."+err.description)
68: }
69: }
70: }
71:
72: function GetXmlHttpObject() // this was just cut and paste from example. If you need
73: // explanation here you're on your own...
74: {
75: var objXMLHttp=null
76: if (window.XMLHttpRequest)
77: {
78: objXMLHttp=new XMLHttpRequest()
79: }
80: else if (window.ActiveXObject)
81: {
82: objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
83: }
84: return objXMLHttp
85: }
86:
87: function updatevalue() // here's the function that actually does the calculation
88: // insert your own here...simple example below
89:
90: {
91: var initvalue = 0
92: initvalue += document.getElementById("value_some_field_qty").value*anotherfield
93: ...and whatever else needs to be calculated....
94:
95: return initvalue
96: }
97:
98: function setupthepageforcalc() //fields where results will be displayed are editable text fields in phprunner
99: // but are disabled and colour highlighted so user cannot change them.
100: // fields used in the calcs have their onchange event set so they trigger the calc
101: {
102: color = "#F1F1F1"; // used later down for distinguishing these fields
103:
104: // make the following fields shaded and disabled so user cannot edit them
105: document.getElementById("value_fieldwhereyouwantresult").disabled=1;
106: document.getElementById("value_fieldwhereyouwantresult").style.background = color;
107:
108: // make the following fields execute the calc onchange...
109: document.getElementById("value_some_field_qty").onchange = function() {docalcofsomesort("13");};
110: }
111:
112: function submitform() //in order to save the calculated resluts in the data base must enable fields
113: {
114: alert("Saving"); // another footprint
115: document.getElementById("value_some_field_qty").disabled=false;
116: document.editform.submit();
117: }
getdataxml.php:
1: <?php
2: header('Content-Type: text/xml');
3: header("Cache-Control: no-cache, must-revalidate");
4: //A date in the past
5: header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
6:
7: $q=$_GET["q"];
8:
9: $con = mysql_connect('localhost', 'user', 'yourpassword');
10: if (!$con)
11: {
12: die('Could not connect: ' . mysql_error());
13: }
14:
15: mysql_select_db("mysql database", $con);
16:
17: $sql="SELECT * FROM yourtable WHERE ID = ".$q."";
18:
19: $result = mysql_query($sql);
20:
21: echo '<?xml version="1.0" encoding="ISO-8859-1"?>
22: <dataset>';
23: while($row = mysql_fetch_array($result))
24: {
25: echo "<id>" . $row['ID'] . "</id>";
26: echo "<date_created>" . $row['Date_Created'] . "</date_created>";
27: echo "<date_modified>" . $row['Date_Modified'] . "</date_modified>";
28: echo "<anotherfield>" . $row['anotherfield'] . "</anotherfield>";
29: }
30: echo "</dataset>";
31:
32: mysql_close($con);
33: ?>
Additional Points by Giles:
1. Each field involved in the calculation or the results must have an id attribute. I used text fields only and modified the include/commonfunction.php to generate the id for editable text fields by inserting id="’.$cfield.’" in the second echo statement as shown below. (Admin, could we have this in the standard product??)
1: if($format==EDIT_FORMAT_TEXT_FIELD)2: {3: if(IsDateFieldType($type))4: echo '<input type="hidden" name="'.$ctype.'" value="date'.EDIT_DATE_SIMPLE.'">'.GetDateEdit($field,$value,0,$secondfield,$edit);5: else6: {7: if($edit==MODE_SEARCH)8: echo '<input type="text" autocomplete="off" name="'.$cfield.'" '.GetEditParams($field).' value="'.htmlspecialchars($value).'">';9: else10: echo '<input type="text" name="'.$cfield.'" id="'.$cfield.'" '.GetEditParams($field).' value="'.htmlspecialchars($value).'">';2. The javascript was added in the header section of the _edit page via the Visual Editor in html mode
1: <script> src="calculation.js" </script>3. Activation of setupthepageforcalc() was done by adding the following line just above the Save/Back To List buttons
1: <script type="text/javascript"> setupthepageforcalc() </script>This function needs to be executed each time the page is displayed to disable the results fields
4. The submitform() function needs to be activated when the page is saved so the onclick event of the save button was modified:
1: onclick=submitform()
For simplicity sake I’ve simplified the amount of data being retrieved and the number of calculations and results fields. also inserted comments and changed a few field/field names to protect the guilty. So be on the lookout for any errors.
Also I created a php page to help create some repetitive parts of the code (e.g. var decarations in the javascript) where multiple fields are involved. Just run the following php and copy/paste as needed. Note the GoodFieldName() function is from phprunner but also forces the goodfieldname to lower case. I thought javascript would not like upper case…
1: <?php
2:
3: //Create the field data section of the getdataxml.php file
4:
5: $con = mysql_connect("localhost", "user", "your password");
6: if (!$con)
7: {
8: die('Could not connect: ' . mysql_error());
9: }
10:
11: $db_selected = mysql_select_db("mysql database",$con);
12:
13: $sql = "SELECT * from your table";
14: $result = mysql_query($sql,$con);
15:
16: $donetablename=0;
17: while ($property = mysql_fetch_field($result))
18: {
19: If ($donetablename==0)
20: {
21: echo "Table name: " . $property->table . ". Entries for xml.php file<br />";
22: $donetablename = 1;
23: }
24: $fieldname = GoodFieldName($property->name);
25: $str = "echo \"<".$fieldname.">\" . row['".$property->name."'] . \"</".$fieldname.">\";";
26: echo htmlspecialchars($str)."<br>";
27:
28: }
29:
30: echo "<br>";
31: echo "<br>";
32: echo "<br>";
33:
34: $result = mysql_query($sql,$con);
35:
36: $donetablename=0;
37: while ($property = mysql_fetch_field($result))
38: {
39: If ($donetablename==0)
40: {
41: echo "Table name: " . $property->table . ". var entries for js file<br />";
42: $donetablename = 1;
43: }
44: $fieldname = GoodFieldName($property->name);
45: $str = "var ".$fieldname;
46: echo htmlspecialchars($str)."<br>";
47:
48: }
49:
50: echo "<br>";
51: echo "<br>";
52: echo "<br>";
53:
54: $result = mysql_query($sql,$con);
55:
56: $donetablename=0;
57: while ($property = mysql_fetch_field($result))
58: {
59: If ($donetablename==0)
60: {
61: echo "Table name: " . $property->table . ". getElementsByTagName entries for js file<br />";
62: $donetablename = 1;
63: }
64: $fieldname = GoodFieldName($property->name);
65: $str = $fieldname." = xmlDoc.getElementsByTagName(\"".$fieldname."\")[0].childNodes[0].nodeValue;";
66: echo htmlspecialchars($str)."<br>";
67:
68: }
69:
70: mysql_close($con);
71:
72:
73: // construct "good" field name
74: function GoodFieldName($field)
75: {
76: $field=(string)$field;
77: for($i=0;$i<strlen($field);$i++)
78: {
79: $t=ord($field[$i]);
80: if(($t<ord('a') || $t>ord('z')) && ($t<ord('A') || $t>ord('Z')) && ($t<ord('0') || $t>ord('9')))
81: $field[$i]='_';
82: }
83: $field = strtolower($field);
84: return $field;
85: }
86:
87: ?>
If someone knows of a better way to approach this or suggestions how to improve or make this simpler, please feel free to comment on the forums: Using AJAX for updating other fields on pages – Forums
Pulling URL from _table_search.php
If you are using PHPR (phprunner) as your IDE to develop your PHP applications in here’s a trick that you are likely going to appreciate at some point in your coding career.
If you utilize the advanced search feature in your applications (not the standard inline search, but the advanced search) and you would like to be able to find the URL for your advanced search results page (with multiple fields included) here’s a way to do it in both PHPR 4.2 and PHPR 5.0. Both of these are provided by Jane at Xlinesoft on the Tips and Tricks forum:
You will first want to pull up your advanced search page {_table_search.php} inside the visual editor regardless of which version you are using.
PHPR 4.2
Switch out this line of code:
1: <form method="POST" action="TableName_list.php" name="editform">
and replace it with this line of code:
1: <form method="GET" action="TableName_list.php" name="editform">
PHPR 5.0
Switch out this line of code:
1: $contents_block["begin"]="<form method=\"POST\" ";
and replace it with this line of code:
1: $contents_block["begin"]="<form method=\"GET\" ";
Adding Form to DB Record in PHPR
I recently had a pretty intensive PHPR project where I had to create a page for a client that called in records from their database as well as displaying a form that visitors could us to contact the “name” and “email” from that particular record. In theory this isn’t a complicated thing to get your mind around but I found that building this in PHPR *PHPRUNNER) was going to be a task for a couple of reasons.
I have had a fellow PHPR user drop me a note and ask me to document how I put all of this together so I figured I would do so here. I am going to prepare some documentation later on regarding two aspects of this project and post them to the PHPR forums later once I have a chance to document everything a little bit better…
The biggest thing that I ran into was issues with Smarty Templates and just inserting a standard PHP Form into them, that was a no-go all the way around for the most part, I did stumble on some ways to do this if anyone is interested but I ultimately didn’t use this on this project. I went back and forth with Jane from Xlinesoft for a while and she was awesome in helping get my mind around this, she also really helped me out a lot with some javascript features that the client was wanting to have with their photo gallery. Xline is awesome! If you build web portals with PHP/MySQL you owe it to yourself to checkout PHPR!
Here’s how I was able to get this particular process to work. I created a new table in the database to store the visitors (people requesting information) information in. I set my permissions so that public could hit this form and store data into it (with validation). Once I created this table and generated it’s add page in PHPR I then went and added that code to the already generated view page from PHPR of the records that I wanted to also have this form on. I then coped my form code into the view page at the appropriate place and saved it out. It took a little while inside the visual editor’s code view to get this to work exactly right but I eventually was able.
The very next part was probably the trickiest to figure out inside of PHPR because if you are familiar with PHPR you will know that you primarily are working with generated smarty pages. In order to add some logic to the form I added this event code in the event editor, (after record added)
1: // Parameters:
2: // $values - Array object.
3: // Each field on the Add form is represented as a 'Field name'-'Field value' pair
4: // $keys - Array object with added record key column values
5: // target_email and target_name are called from the existing database
6:
7:
8: //********** Send email with new data ************
9:
10: $email="".$values["target_email"];
11: $message="";
12: $subject="Resort Inquiry - ".$values["target_name"];
13: $from="Client's Name <info@clientsdomain.com>";
14:
15: foreach($values as $field=>$value)
16: $message.= $field." : ".$value."\r\n";
17:
18: //Headers
19: $headers = "To: " . "\r\n";
20: $headers.= "From: $from" . "\r\n";
21:
22: mail($email, $subject, $message, $headers);
23:
24:
25: //********** Redirect to another page ************
26: header("Location: http://www.clientsdomain.com/confirmation.php");
27: exit();
After I added this and tested the form it was working great. Here is something that I probably need to make sure I mention. In my trials building this particular app, I learned that i had to go in and predefine the value for 2 hidden fields in the form, one was the “name on the record” and the “email on the record”. Once i had this part knocked out everything worked great. This is pretty good to know because there wasn’t a whole lot of information on the forums when I started trying to build this particular app and I had to hunt around and find info from various places. As I mentioned, Jane at Xlinesoft was awesome to help me out with this project.
PHPR Project Management
I found this interesting post on the Xlinesoft forum tonight. (Best Practices) It relates to Best Practices for using PHPRunner & Subversioning. I currently don’t have a use for this in anything I am working on but thought that it was pretty interesting so I decided to include it here…
——————————————————
1.0 Summary:
Setting up an SVN Repository significantly increases manageabilty of large scale projects using PHPRunner across many environment instances and developers. SVN Repository will also encourage companies to use PHPRunner to be used in larger scale projects. The output codes can be deployed based on the subversions and the config file can be determiend upon staging.
2.0 Case Statement
I have a project that is used in the development and live environment. Initially, it was not an issue since the db schema was synched. As the the dev version is release to production, and additional tables were added to the dev environment, I found it almost impossible to manage the project with my simplistic approach. What I ended up doing was for each instance of the development (dev, test, stage and live), I created seperate projects. so, I now have 4 individual projects. Our european counterpart saw our US project and of course, they requested for each of their instances which of course was about 6 all together. So, now I have a total of 10 seperate project for each db instance of our development phase. In my mind, the only difference is the config (i.e., host, user, password and db name).
For awhile, I would just select one of the projects, make my modifcations, and then save as for each instance of the project. This worked for awhile.The problem started to occur when we went live the first time. There were enhancements that I wanted to make for the production version. Meanwhile, the db schema in the development and test instances started to change. So, I found many of my links started to break. So, I could no longer just simply Save As. The next step was to Cut and Paste.
I stopped Saving As, and started doing the following. I would make the changes in one of the most complete instance which usually was the development instance since its where the new tables are comming from. Then, I would open another PHPRunner and open one of the other instances that I wanted to update. Of course, I couldn’t update all since some of the test and stage instances have not been updated.As a result, there was chaos in my codes. I no longer didn’t remember which project belonged to what instance. I didn’t know which one got updated and which ones are the same and which ones are broken. In other words, the project(s) became extremely unmanageable.
3.0 Analysis
This is a typical result when what seemed to be an easy process ended up entangled. Many times, the simpliest approach many not be the wisest approach and could cause havoc on project and resource management. The criteria for a simple project is if the proejct is being developed by a single develper on one or two instance of the same project. Both db instance must be synched for most of the time or at least a push to keep it in synched. As soon as a third unsycnronized db instance is introduced, then it can no longer be considered as a simple project management case. In order to avoid the pitfall of the case statement, one must ensure scalability, flexibility of code management, as well as the deploy process.
4.0 Approach
4.1 Set-up
The way to resolve the issue was we deployed a subversioning tool, SVN with a UI frontend, Tortoise. We created a project folder in the SVN Repository and a trunc.
I selected the best “version” from the projects, which we’ll call ProjectPHP. I then looked for the PHPR project file and copied that and the tmp and visual folders into my SVN trunc folder (which was also in my local apache htmdoc webserver path). I opened PHPRunner, make the changes I wanted and Save. I am now saving the PHPR file into my SVN trunc folder that is also within my apache webserver folder. I set my output file directly onto my trunc folder and I set the preview with the localhost url. I build the projects.4.2 Work Process and Project Management
If all goes well, all the output files is created within the trunc folder. I use the preview just to see if anything was broken. When I am satisfied with the changes, I close PHPRunner and then go to the trunc folder. I delete the tmp and visual folder since I discovered that once I successfully build an output files with the tmp and visual folders, I can erase those two folders. Somehow, when I open PHPRunner and build it again, PHPRunner knows where to look for those tmp and visual folders. Also, for some reason even if I delete these folders, when I go to my Visual Editor, I can still see all the icons.
I committ all the files to the SVN Repository. (Note: It’s realy easy to do it using Tortoise SVN). I know have a trunc of my project. I then check out a Working Copy. This is a good idea since you don’t want to mess up your trunc version since it will always be your “base copy” of the code. In my working copy, I can open PHPRunner and open that particular project file. It works beautifully. All the icons show up in my visual editor. I can change the db configuration so that I am developing to the relevant instance of the database. Once I am done, I save my I used the Live instance of db to create or update the project. Once I was satisfied, I save my project which was the latest and greatest of the codes that I have been chaotically managing. Once we created the trunc, we created tags and branches.
I checked out the trunc into my Working Copy folder. For now, my WC is pointed towards the trunc. For every major milesone I reach, I create a Tag. The naming convention for the tag is usually the date and some task indicator like Dec102008_Task_1of3 or some other meaningful naming convention. I continue with my work on my WC until I complete all my tasks. By now, I have 3 tags, since I created them for each milestone.
I then committ my changes into the SVN Repository. Since I was pointed towards the trunc, it updated the Trunc. But since it is also a completion of a version of ProjectPHP, I created a Branch and called it Revision 1.0. This is my first “subversion.”
For now, I can continue to make small modificaitons or bug fixes to Revision 1.0. It is important to note that I am working constantly on my WC (Working Copy). Basically, I can “Switch” the WC to the trunc, tags or branches. When I am working on Revision 1.0, I will make tags along the way in order not to lose any of my codes in cases I mess up further along the way. There is a stopping point. Once I am done with my changes, I create a subversion Revision 1.1
Note: Please refer to the sources link below for more detailed subversion workflow.
4.3 Deployment
For every completed revision, I test it in the Test, QA, Stage environment. And once it passes all 3, then I deploy it to Production. We created a page that allowed me to selecte which subversion I want to deploy and the “dbcommon” version that I want to use. Since in PHPRunner, the db information is part of the editing the project, when it builds the file, it also builds the dbcommon that has all the db information (user, password, etc.). So, when there is a different db login for each instance, you will have to specify the correct “version” of the dbcommon. All I did was to create one dbcommon as a “trunc” and then created all versions of that for each instance of the db.
Whenever I deploy, I select the code that I wanted to put into dev, qa or stage, and select the appropriate dbcommon. It is a dropdown. The staging process basically takes all my files which are the output files and overrides the dbcommon with whatever “version” of the file I select. Remember the version is realy just to be able to have all the dbcommon for all the instances.
Also, there is one caveats for those that may want to have a others use the project for each of the instance. So, say if you have a Live version with the old db schema. So the Live version is Revision 1 and all the subversions. The active development is already on Revision 3, for example. And you’ve made some changes on Revision 3 and deployed it to your develpment and stage instances (and not Live of course, because the latest db has not been deployed to Live).
Since you still want to test and stage your Revision 1 code, you can deploy the codes to the development and test environments but use the db instance that has the matching schema (usually by now, it will be just the Live version). Notify the folks using the tool that you’ll be testing and staging a production fix and therefore need to log out. Once you verify in test and stage the codes, deploy the code to Live. At this point, unless there are major functionality and not just small fixes, you may have to merge it with either Revision 3 (the latest and greatest version of your project) or back to the Trunc.
I suggest not to do this however. I would just leave Revision 1 and all its subversions where it is. It is easier just to make sure that that functionality is copied over to Revision 3 and then merged back to the trunc (since Revision 4 may be under way).
5.0 Conclusion
When you use SVN with Tortoise and set up the appropriate subversions, your project with multiple developers and instances can be manageable, as long as everyone understands the subversioning principle. Keep the PHPR file with the output files and delete the tmp and visual folders once you have successfully built a project within the svn file folder. The PHPR file gets subversioned as well. In other words, it becomes protected for any accidental mistakes (like I have done so many times with my manual management of my php projects.)
I hope this post will encoruage to use PHPRunner at a large scale basis and will encourage the makers of PHPRunner to make it more and more robust.6.0 To Do’s
- Set up URLs for each project intance (dev, test, stage, live). These corresponds to the db instances.
- Set up SVN repository
- Set up staging page. There will be a list of the environment where the codes will be deployed, a list of the code version to deploy, and the dbcommon version to override the dbcommon built along with the PHPRunner.
- Set up Project Folder then /trunc, /tags, /branches (Please see the link and study the manuals)
PHPRunner 4.2 released
I am just now getting around to updating my PHPRunner Software on my Dev1 Machine (have been really behind the times lately) and was surprised to find that they have added quite a few new features that are very handy one click type of things, here’s a list:
- Dynamic permissions
- Import data from CSV and Excel files
- Resize images on upload and iBox support
- Auto-update fields. Automatically insert a value into field every time record is updated (UpdateBy, UpdateTime, IP address of user etc)
- Visual SQL Editor
- Add new item to dropdown box as a fully-featured add page in popup
- Only allow complex passwords (mixed cases, non-alphanumeric characters)
- Registration confirmation via email
- CAPTCHA
Probably the first one on the list, Dynamic Permissions, is the one that I have most anxiously been awaiting. I can see how this is going to be very nice. I have been running 4.2 on my notebook pc for a little while now getting acclimated and it operates great, the new enhancements to the visual sql editor are most welcomed!
I know that I have a few folks that hit my blog from time to time that are also hardcore PHPR users, I would be interested in hearing from any of you and your take on the software as well. Just email me or drop me a comment / trackback to your blog on here…
Source: PHPRunner 4.2 released – Forums
URL String w/ Advanced Search – PHPR
I ran across something today that was pretty cool. Let’s say you are using PHPR to generate your PHP pages and code, the advanced search feature is setup by default not to give a URL in it’s results (I have wondered why this is because basic search gives you a workable URL string for building links, etc.). This would only be handy I guess if you were wanting to narrow a search down by more than one field (real estate sample: “brick homes” in “x city”, or “wood homes” in “y city”, etc.). Well, Jane posted something to the tips and tricks section of PHPR’s support forum that allows you to go in and tweak out the code before it’s generated so the Advanced Search also will return a URL string that you can use in building your links. Here’s what she posted:
Proceed to the Visual Editor and open the advanced search page.
Switch to HTML mode and find the following code snippet:1: <form method="POST" action="TableName_list.php" name="editform">Replace this line of Code with this:
1: <form method="GET" action="TableName_list.php" name="editform">Once you have changed this one line of code, then go back into your project and run the advanced search, the URL will then be available for you to paste into your code.
Source: How to get the URL of advanced search results page – Forums








