Computer Magic
Software Design Just For You
 
 

PHP Tutorial – Lesson 13 Looping, looping, looping, looping, looping, looping, looping, looping…

February 17th, 2006

There are 4 main concepts that you have to understand inside and out to be a good programmer.

  • Variables
  • Arrays
  • Choices (if statements)
  • Looping

We have talked about variables (those named containers where we can store information) and we have talked about choices. We have also touched on arrays (remember $_GET and $_POST?). Remember arrays are variables that have multiple slots and use the [] to designate wich slot to set or get.

Now, all of this is very nice, but it is the looping where computers really show their stuff. The ability for a computer to do something repetetavly as fast as it can is where computers flex their muscles. We call these chucks of repetative code loops. Loops can come in many different variations, but they all ultimatly repeat the same steps over and over again until they are signaled to stop. You will see that loops and arrays can work very closely together to process large ammounts of data very quickly.

In this example, we aren’t just going to show you the standard looping demonstrations. We are going to jump right in and show you a real world example! The idea here is to get you up and running fast so that you can do something useful. As you become more familliar with the looping structures, you can branch off and try things on your own. Soon you will look at code and you will read it like a book.

For our real world example, we will do a database query and show you the results of that query. We will use the SELECT statements from the previous article to display a list of users.

One thing to note before we continue though is that this code does not have any authentication built in. If you take this demo and point it to a real table with real information, you could be exposing that information to people who shouldn’t see it! That is why we use a demo table that has no real information in it.

To get information from the database and display it on the screen, we will have to do several steps.

  1. Connect to the database
  2. Create your SQL statement
  3. Send your SQL statement to the database
  4. Start a loop that will read each row returned from the database
  5. – In the loop, print the information for this row to the screen so the user can see it
  6. Close the database connection
  7. Done! The page runs out of code and the user sees the page

For this example, create a php file called user_list.php and start filling it with the following…

Step 1 – Connect to the database
One of the cool things about using a real database server is that it has all sorts of security built in. You will need to use your database user name and password. This may be the same as the user name and password you use to login to your account. Best practices say that you should create a new account specifically for the web application that can only do what it needs to (unlike your account which can do most anything to the current database). In most ISP setups, you can not create a new account directly, you will need to use the provided control panel to do so. Instructions will vary depending on the control panel software you use, so I will leave this as an exercise for you. For our example, if your current username and password work, then we can just use that.



<?php
mysql_connect("localhost", "user", "password");
?>

The mysql_connect function requres three parameters. The first is the server to connect to. This is a host name or an IP address. Since the PHP script actually runs on the web server itself (and not on the browser viewing the page) the address should be relative to the web server. In many ISP configurations, the database software is running on the same box, so localhost will work for the address as localhost always means “me” or the current box. It is a special loop back address present on any machine with the TCP/IP protocol installed.

The second and third parameters are the user name and password that is required to connect to the database. This is important to keep unwanted users from connecting and looking at or changing your data.

Finally, there are some optional parameters available. We won’t talk about them here, but you might want to look them up in the PHP documentation (http://www.php.net).

Creating an SQL statement
Once you have a connection to the database, you can send it requests. These requests are formulated using the SQL langauge. We have already covered the core four statements (INSERT, UPDATE, SELECT, DELETE). If you are this far in the tutorial series, you should have even practiced with sending these statements to the database via a GUI interface (PHPMyAdmin or SQLYog). What we want to do now is have our program (PHP script) create an SQL statement and send it to the database.

If you remember back to our lesson on variables, we always put “s around values that are not numeric in PHP. PHP itself doesn’t understand SQL and therefore it considers SQL statements data. Anything that is data (and isn’t a number) needs to be surrounded by “s. In PHP, you could surround data with single ‘s, but that has a slighly different meaning, so generally we always use double “s. For this reason, we generally use single ‘s in the SQL statement itself. Lets illustrate the point so that you see why we do things this way.

DO NOT PUT THIS SNIPPET INTO YOUR PHP SCRIPT



<?php $variable = 'Don't do that!'; ?>

In the above snippet, that is PHP code. Notice that I used the single quote to surround the value. Do you think that this will work? Try it out and see. You will get an error.

PHP sees the first quote and say to itself “this is data, I can pretty much ignore it”. Once it sees the ending quote, it says “ok, the data is done, now the next stuff should be valid PHP code”. The problem is that the single quote is also what we call an apostrophe. The computer is dumb and won’t try to figure out if the next quote is a valid punctuation in the data where it resides, it will simply say, “ahh, the quote is closed, now run PHP code!”. What you get is a PHP engine try ing to understand what it should do with (t do that!). That isn’t a PHP command and therefore messes up the script and causes the error. If we change this example just slightly, it will run just fine.

DO NOT PUT THIS SNIPPET INTO YOUR PHP SCRIPT



<?php $variable = "Don't do that!"; ?>

By using the double quote, PHP won’t end the data until it encounters another double quote. Whichever quote it starts with, it will expect the same type of quote to end with. This allows you to have all the single quotes inside double quotes that you want!

The point of this is that SQL statements themselves need quotes also for their values. Thus, we use double quotes for PHP data and single quotes for SQL data, and everyone is happy!

Here is an example that will request all the users from the database. Put this in your php script under the mysql_connect statement (between the open and close PHP tags).



$sql = "SELECT * FROM mytable";

It is mostly that simple.

Send your SQL statement to the database
Now that we have created our SQL statement, it should be ready to send off to the database server. Generally when you run a SELECT statement, you want a list of matches back. This comes in the form a record set. It is just a bunch of rows. Here is the code to send the statement to the server and get back a recordset. Add to your PHP script under the last statement ($sql = …).



$rs = mysql_query($sql);

The $rs varaible will hold the recordset when the database gets done. The mysql_query function requires one parameter which is the SQL statement. Our $sql varaible holds that statement (look at the previous line). It is nice that we don’t have to worry about sending packets on the network or parsing responses. The mysql libraries wrap all this up in a few easy to use function calls. Note that we have left out error checking in the intrest of keeping things simple. As the tutorials progress, we will start to add that back in so that you can see what proper error checking should look like.

Start a loop that will read each row returned from the database
Now that we have our recordset, we can start getting the records out of it. There could be one record, or there could be thousands. You don’t want to write the same code over and over again to exrtract the records. Instead, we use a loop. The loop will grab a record and display it. Once it runs out of records, it will quit. Looping is great and is one of the things that computers do best! Put this code under the last statement.



while ($record = mysql_fetch_array($rs))
{
}

This is a while loop. It continues to loop until the expression is false. The expression is the part between the initial ( and ). The expression in this case evaluates the $record variable. When you tell mysql to fetch a row from the recordset (mysql_fetch_array($rs)) you are returning an array of columns for the current row. This is great as an array of columns is easy to work with. When the recordset runs out of rows, the $record variable will be equal to false. Since the expression for the while loop is based on evaluating the $record variable, the false value will cause the while loop to quit looping (it only loops while the expression is true!). Boolean is weird (that is true/false) in that zero is false and anything else is true. We take advantage of this by allowing our loop to continue as long as there is a valid array of columns, but when we run out of rows, instead of a valid array of columns, we get a false value.

In the loop, print the information for this row to the screen so the user can see it

Running this script now will show you nothing. We didn’t bother to print anything! We can display information back to the user by printing during each iteration in the loop. Replace the previous snippet of the loop with this one.



while ($record = mysql_fetch_array($rs))
{
print "hi";
}

This loop will print “hi” as many times as you have records in your table. Go ahead, give it a try. Notice how it just prints “hi” one right after the other.

Now, lets try it again with information from our recordset. Again, replace the previous snippet with this one.



while ($record = mysql_fetch_array($rs))
{
print $record["user_name"];
}

This will print a list of users. We didn’t use any HTML to make it look nice so again, just like with the previous snippet, it all prints on the same line. Now lets make it look a little nicer. This is where we start adding HTML tags to our print statements. Since HTML is not PHP, we need to put it all in quotes just like everything else. Replace your last loop with this one.



while ($record = mysql_fetch_array($rs))
{
print "<li>" . $record["user_name"] . "</li>";
}

This will use the HTML LI tags to display each item as a list item. This creates that nice dot and puts each item on a seperate line. This makes it a bit more readable, but it can’t do much. One of the cool things about web pages is that you can create a hyperlink. This means that when some one clicks that link, the browser will go to the specified page. To create a link, all we have to do is place the appropriate tags and the target pages. Here is another revision of the loop with a link that will take us to a different page. This page will display the details of the specified user. We will create the target page in the next tutorial.



while ($record = mysql_fetch_array($rs))
{
print "<li><a href='user_details.php?id=" . $record["id"] . "'>" . $record["user_name"] . "</a></li>";
}

This final version of the loop renders the list of users and turns them into hyperlinks. After the next tutorial, you will be able to click the user and have it display their details. For now, clicking the link will give you a page not found error (we haven’t created the target page yet!).

Close the database connection
Once you are done, it is a good idea to close the database connection that you have opened. Here is a complete copy of the script including the final close statement.



<?php
mysql_connect("localhost", "user", "password");
$sql = "SELECT * FROM mytable";
$rs = mysql_query($sql);
while ($record = mysql_fetch_array($rs))
{
print "<li><a href='user_details.php?id=" . $record["id"] . "'>" . $record["user_name"] . "</a></li>";
}
mysql_close();
?>

Old MySQL API vs the new MySQL API
MySQL recently released version 5 of their database server software. This version included many new features including views and stored procedures. The makup of a stored procedure is basically a function call to the database. As such, many of the pieces of information that you used to put into an SQL string can be passed as a parameter now. The major difference is in how you call these stored procedures from your code. The new MySQLI interface supports all the new features while the old MySQL API only supports the old way. In our exercises, we are using the old API. Many ISP’s don’t have support for MySQL 5 yet and you may have to write your code using the old API, but it is good to know what is coming in the near future and be prepared for it.

Give this script a try. We may come back and modify it later. In our next tutorial, we will write the user_details.php script to finish illustrating the point on how to transition from the listing page to the details page.

Ray Pulsipher

Owner

Computer Magic And Software Design

PHP Tutorial – Lesson 12 Hello!? Are you there?! – Using the SELECT statement

February 13th, 2006

This is both the easiest and most difficult of the four SQL statements. The SELECT statement.

We have covered putting data in the database (INSERT), removing data (DELETE), and changing data that already exists (UPDATE). Aside from the INSERT statement, which simply jams data into a table, you append a WHERE clause onto your statements to help specify which rows you want to effect (eg: you generally don’t want to delete ALL records). The SELECT statement uses those same WHERE clauses. In a SELECT statement, the WHERE clause can be so very simple or it can be the most mind bending part of working with a database. I sugest you keep it simple where possible.

Lets start with the simplest of SELECT statements. When we remove information using the DELETE statement, it is VITAL that we use the appropriate WHERE clause or we could end up loosing more data than we intend. With a SELECT statement, leaving off the WHERE clause just gives us every record in the table. How much of a problem this is depends on your current situation. For instance, we created a user table. If a user is updating his profile information, it is pretty vital that the user only gets his/her information.

Ok, here is the SELECT statement. Place this into your SQL admin software (PHPMyAdmin or SQLYog).



SELECT * FROM mytable

From this statement, you should get all the records you previously entered. If you went crazy during the DELETE tutorial, you may not have anything left. Refer to the tutorial on INSERT or use your SQL GUI to add a few rows.

Lets break down the current SELECT statement. We start with our statement, SELECT, so that the database knows that we want to get information. Next we follow that with the * character. This is called a wild card and means give me all of it. For those of you who have worked with a dos or linux/unix command line, you will recognize this character. When asking for information, you can limit the colums that are returned. If all you wanted was the user name, you could keep it from returning the password. This can enhance security and make the network communications more efficient. Lets say you stored files in a database. These files could be quite large. If all you want is the information about the file, it could save quite a bit of network bandwidth to limit the number of columns returned per row. It is important to note that the * character referrs only to the columns you want and does not effect the number of rows returned (that is taken care of by the WHERE clause).

Here is a SELECT statement in which only the user_name column is returned.



SELECT user_name FROM mytable

Here is the same SELECT statement that returns the user_name column and the age column.



SELECT user_name, password FROM mytable

You use the comma to seperate the column names.

Just like the other SQL statements, you have to specify which table you are talking about. Which table should we get the information from? With a SELECT statement, you can do what we call a JOIN and get information from multiple tables. We won’t cover that here both because it would make this article very long and because it would complicate things greatly (maybe in another tutorial later). The reason I bring JOIN up now is that if you use a JOIN, you may have a comma deliminated list of tables. For our exercises though we will only have one table specified after the FROM clause.

That is the basics of the SELECT statement. Our examples just return every row in the table. Some times this is appropriate (if the administrator wanted a complete list of users in the system). Often you want to limit the information returned. Lets say that you only wanted to look at users who were older than 18 years old. Lets say you are sleezy and want to send out inapropriate emails to all your users, but because of the laws, you better not send these emails to users who are too young. I know this scenario sounds bad, but it happens all too often in the real world. When you sign up for some services, if you report that you are younger, these services may not let you into certain areas or may take extra steps to protect your information. Here is the statement that would grab all users who were 18 and older.



SELECT * FROM mytable WHERE age >= 18

If you refer back to the tutorial on If statements, you will see that expressions in the WHERE clause are very similar. The cool thing about WHERE statements in SQL is that it will automatically apply this expression to every row in the table. All rows that are true (the age is greater than or equal to 18) will be returned. For this statement to appear correct, you will need a few users in your table of varying ages (some below 18 and some above).

Lets pretend that we are looking at the user list for Hotmail. The base list would be millions of users. If you cut out everyone below 18, you still have millions of users! Lets limit things a bit more. Lets get everyone 18 or over whos user name starts with the letter A.



SELECT * FROM mytable WHERE age >= 18 AND user_name LIKE 'a%'

In this statement, we say that the user has to be 18 or over (age >= 18) and the user_name has to begin with A (SQL text matches are case insensitive). Instead of using the user_name = notation, we use the LIKE keyword. This allows us to use the % symbol as a wildcard (a+anything else). If we would have put user_name=’a’, then we would have only gotten records where the user name was just the letter a! Only records where both parts of the expression are true will be returned. This is the kind of SQL statement you use if you want to have A-Z links where you can click A and get all of the A’s.

The WHERE clause is very powerful. Lets now say that you know you are dealing with a user that has the ID of 1. To get this user, you can do this.



SELECT * FROM mytable WHERE id=1

This will return just the record where the field called ID = 1.

There are many many more things you can do with a SELECT statement. Check out the GROUP BY and LIMIT clauses if you get a chance in a MySQL reference (http://www.mysql.com). I will be showing more examples later when we get back into PHP coding and show you how to integrate PHP with MySQL to produce data driven web sites!

Again, because of the versatility of the SELECT statement, it can take quite a while to master. Always try to keep things simple. Some of these features are great, but they should not be over used. That only leads to headaches and missed deadlines 🙂

Ray Pulsipher

Owner

Computer Magic And Software Design

PHP Tutorial – Lesson 11 Just make it go away! – Using the DELETE statement

January 31st, 2006

The next of the big four SQL statements that you need to master on this long and bumpy road is the DELETE statement. At some point, a customer will leave, or an item will permanently go out of stock or some other reason will pop up where you will need to remove data that is no longer relevant. This is where the DELETE statement comes into play.

A side note on removing old data. The Sarbanes-Oxley act imposes many new rules on the collection of financial data. This can directly effect the jobs of many overworked database administrators and programmers. It is important to realize that when you use the DELETE statement, the record is gone and will not come back. If you need to keep records of old transactions, it is good to have another method in place to hide deleted items without actually deleting them. One popular method is to have a field where you can mark the item as deleted rather then actually removing it. Another is to setup an identical table and move entries to that table rather then just release the data into the void never to be seen again. For more information on how this act can burden you in your work, check out http://www.sarbanes-oxley.com/.

On to the show…

The DELETE statement itself is very similar to the UPDATE and SELECT statements, and has certain similarities to the INSERT statement also. Here is an example of a DELETE statement.



DELETE FROM mytable

Again, our SQL statement starts with the command (DELETE) then tells the databas which table we want to remove data from. In this case, you will remove ALL records from the table. You very seldom want to actually do this. If you do want to remove all records in a table, TRUNCATE TABLE is a much faster implentation as it is super optimized to remove all data from a table (but can not be used to remove individual rows!).

What we would really prefer is to remove only certain rows. Here is an example of a DELETE statement that only removes one row.



DELETE FROM mytable WHERE user_name='bobs'

A common theme in SQL statements is that without a filter (WHERE clause) the statement applies to ALL records. It is therefore important that you make sure to always use a filter on DELETE and UPDATE statements (unless you are really really sure you don’t want one!). Your boss at amazon.com isn’t going to like to hear “oops” when you accidentally DELETE all customer records 🙂

In this last example, we specify that we will remove any and all records where the fields user_name contains the vvalue ‘bobs’. If you happen to have two users in this table with the same user name and that name is ‘bobs’, then DELETE will remove two records. This is why it is common to have an ID field in all tables. What if you didn’t want to allow two users with the same user name. How do you get rid of the correct one? With the ID field, each one should have a unique number and you can therefore supply the correct ID of the user in question.



DELETE FROM mytable WHERE id=5

Again, this will DELETE all rows that match this criteria. With an ID field that is numeric and has auto_increment set, you should be able to identify each row uniquely.

Is this looking similar to the UPDATE statement at all? It should. All the WHERE clauses follow the same logic and syntax for UPDATE, DELETE, and SELECT. You don’t need a WHERE clause for INSERT since you are adding data.

Also, notice that we don’t use the SET keyword as we aren’t changing any data, we are simply removing the data we don’t want.

Deleting without deleting…
As I mentioned earlier, the Sarbanes-Oxley act has added a lot of stress to many database administrators and programmers. How do you delete a record without deleting it? We will show one way here.

Rather than use the DELETE statement, add an extra field to your table. Call it ‘deleted’ and make it a bit field. Set the default to 0 and choose the NOT NULL option. It would be easiest to use one of your GUI front ends to do this (PHPMyAdmin or SQLYog). You could use the ALTER TABLE command to do it manually, but you will have to look up the syntax.

By setting a default of 0, all new records as well as all old records will start with the value of 0 (false). To remove a record, we don’t have to use the DELETE command, we can use the UPDATE command and change the field to equal 1 (true).



UPDATE mytable SET deleted=1 WHERE user_name='bobs'

We have just marked bobs as a deleted user. We may also want a field that will hold a Date/Time. This way we could keep track of when the user was deleted. We might even want a field to keep the user name of the person that deleted this user. This would give us the ability to audit actions taken in the system.

Now, we didn’t really DELETE the user. This is just an illusion. When we want to authenticate the user, we would want to add into the WHERE clause an expression that would only allow users who were present and who hadn’t been marked as deleted to show up. By using these WHERE clauses, we can make sure to keep up the appearance of the illusion by not showing the deleted records where they should be hidden. We will talk more about the SELECT statement in the next article and continue this scenario.

Until then, keep working with the DELETE statement and try different expressions in the WHERE clause. Understanding the statements themselves (UPDATE, DELETE, INSERT, SELECT) is easy. It is the WHERE clause that trips people up. Rember that the expressions in the WHERE clause are the same basic thing that you would write in an IF statement in PHP. In database land though, the variables are the field names in question and the expression is evaluated against each record.

Ray Pulsipher

Owner

Computer Magic And Software Design

JSON – Another AJAX?

January 30th, 2006

Here is an interesting article on JSON (http://www.informit.com/guides/content.asp?g=xml&seqNum=268). It has some great examples of what JSON really is and how it works.

Before we go any further though, we should spend some time discussing what exactly AJAX and JSON are.

AJAX is simply a new way of thinking in the JavaScript programming realm. In the past, we refreshed every web page any time anything changed. To get a more application like feel, an AJAX library allows you to easily create pages that appear to work without the refresh. There is still a refresh, you just don’t have to refresh the whole page. All that stuff you learned about GET and POST when learning web programming still applies.

All the browser companies added a new object that Javascript could instantiate. This object had the ability to make requests in the background. It is this core functionality that AJAX relies on. An AJAX library doesn’t do anything that you can’t do yourself, it just makes it MUCH easier and saves you from having to write and debug the plumbing code. Other than usign the new object, AJAX isn’t really even new technology (it is just a JavaScript file with some pre-built functions). The real shift is in the way you think. Now, instead of writing your forms so that they refresh the page, you write them so that only portions of the data are refreshed.

That is AJAX in a nutshell. I personally like to use the prototype.js library myself, though there are others out there and most of the big name companies are integrating AJAX support into their products.

The acronym stands for Asyncronous JavaScript and XML. Often the requests that an AJAX script will make will be returned as XML which can be easily parsed. Again, not a new technology, just put together in a new and interesting way to leverage existing technologies and make things work in a new way.

Now, on to JSON. JSON is not like AJAX. JSON (JavaScript Object Notation) is a way to describe objects and their properties in a string format. This allows you to package up an object and its data and send it from one location to another. JSON itself does not provide a mechanism that will allow you to transport the information, but it does provide a mechanism that will allow you to deal with the information once you get it. The article above shows the first few examples without the page retrieving information from another site. Notice that you can use JSON without a JSON library if you use the eval() function in JavaScript. Use of the eval() function in JavaScript can be very dangerous and therefore a JSON library is useful (it still uses eval internally I am sure, but it checks the code to make sure things are safe).

JSON is positioned to compete with protocols like SOAP which was designed to allow applicaitons to communicate easily. In the example article above, the author takes advantage of the src property of script tags to fool the browser into making a request (similar to AJAX but using a different method). I imagine you could use this same technique with AJAX to make requests to the remote server. The down side to this is that all requests are made via a GET request where AJAX can support GET or POST requests.

JSON is not positioned to take the place of AJAX, they are really different animals, but I can see the potential of utilizing the two together. Rather than have to resort in trickery to get your JSON information from a remote site, have your AJAX request return JSON information and send it to the JSON parser. This could potentially save you some coding when trying to parse the XML information returned via teh AJAX request.

Note, that the URL that you request can return whatever you make it return. Personally when using AJAX, I mostly just return rendered HTML so that I don’t have to write more javascript to do the same thing (not always the answer, but in my case it works great!). I can see JSON as a great compromise as the ability to have an object ready to go would allow me to write just a few lines of code and have the same end effect of working with all that XML. Don’t get me wrong, XML is great, but why make yourself work harder than you have to? If a quicker, easier method works, do it and save yourself some coding and debug time.

Correction: During further review of the prototype AJAX library, I noticed that it posseses JSON capabilities already. Something to keep in mind anyway.

All in all, some very good examples, go read the article!

Ray Pulsipher

Owner

Computer Magic And Software Design

There is no substitute for quality service.

January 26th, 2006

Since starting in this business many years ago, one thing has been the key to my success. Good communication with a smile. So many times we as techs get lost in our acronyms and the technical details that non techs get lost in the conversation. The ability to communicate effectively with the rest of the world can mean the difference between a satisfied customer and a disgruntled customer, regardless of the quality of the product itself.

This is a very important lesson that many techs never learn. This is the reason that many companies have a sperate sales force from the tech staff. It is also the reason that at many companies you never get a chance to talk directly to a tech.

Generally, as a tech, I enjoy a priveleged status in that other techs will talk to me. As a programmer with a number of years of experience, I can generally carry on semi intelligent discussions in most computer related fields. In the not too distant past, I had the opportunity to converse with a programmer from another company. The fact that I was a tech did not spare me from bearing the brunt of his ego. I was amazed at the lack of support I recieved from the guy who was supposed to have written the software in question. After such treatment, I was not suprised that emails went unanswered and that a solution to the current problem has yet to be found.

Regardless of the quality of the product, the significant investment and difficulty of switching to a new system is often all that keeps a client from jumping ship. If service is poor for too long, it can actually create a large enough pool of disgruntled customers that a competitor can sweep in and become instantly profitable even if their products are sub standard.

Here at Computer Magic, our customer base is built primarily on word of mouth advertising and our current clients know where to come when they again need more of our services (and the do!). It just makes good business sense. Do yourself a favor and make your work day more pleasant by leaving those you work with in better shape (emotionally as well as professionally) then when you started. You will find that it not only will make your day go faster, but it will be good for business 🙂

On the same subject, one of the things that we have been week in is our customer service for web hosting. Not because we weren’t there for our clients, but because we were not the ones offering the hosting. The breakdown occurs because we offer our level of service which is often unmatched by the hosting company involved. To remedy this situation, we finally got things in gear and we are now offering great prices on web hosting packages to our clients as well as to the general public (https://www.cmagic.biz/wordpress/web-hosting). Our clients can now pay for their complete package (hosting, design, database, programming, secure certificate, domain name) all in one invoice. No more sending our clients to fill out techinal details to get a secure certificate or to register their own domain name. They have a business to run, they don’t want to be bothered by setup/renewal fees at three different companies.

The discussion about offering hosting has been going on for over two years. Our initial argument was that we concentrate on other aspects of the web site process (design, development, etc.) and we left the actual hosting to others. The final push to offer hosting services was not so much a financial decision, but a customer satisfaction decision. This tech mumbo jumbo is confusing and time consuming for our busy clients. We can now take the burden off their shoulders and allow them to simply enjoy their new site.

Make every experience a pleasant one!

Ray Pulsipher

Owner

Computer Magic And Software Design


Home | My Blog | Products | Edumed | About Us | Portfolio | Services | Location | Contact Us | Embedded Python | College Courses | Quick Scan | Web Spy | EZ Auction | Web Hosting
This page has been viewed 870737 times.

Copyright © 2005 Computer Magic And Software Design
(360) 417-6844
computermagic@hotmail.com
computer magic