Matthew Steven Kelly

Yahoo Hot Jobs

December27

Yahoo had an interesting article about what is “in” and what is “out” when creating a professional resume. To quote the article “Fashion changes, and resume styles change, too. If you have solid skills and work experience but your resume isn’t getting any bites, you might need a resume makeover.”

http://hotjobs.yahoo.com/career-articles-the_new_resume_rules_what_s_in_and_what_s_out-1056

To summarize the article:

  1. Include a “Professional Summary” on the top of the resume instead of an “Objective”
  2. Make it easy on the eyes
  3. Customize the resume for the job
  4. One-page resumes are a myth
  5. Quantify your accomplishments
  6. Include website links to previous employers and possibly a brief description of them
  7. Include LinkedIn or other social networking site links at the top of the resume

PHP Best Practices

December26

This was a very informative article: http://www.odi.ch/prog/design/php/guide.php

Best practices

This guide will give you solutions to common PHP design problems. It also provides a sketch of an application layout that I developed during the implementation of some projects.
php.ini quirks
Some settings in the php.ini control how PHP interpretes your scripts. This can lead to unexpected behaviour when moving your application from development to the productive environment. The following measures reduce dependency of your code on php.ini settings.

short_open_tag
Always use the long PHP tags:
Do not use the echo shortcut

asp_tags
Do not use ASP like tags:

gpc_magic_quotes
I recommend that you include code in a global include file which is run before any $_GET or $_POST parameter or $_COOKIE is read. That code should check if the gpc_magic_quotes option is enabled and run all $_GET, $_POST and $_COOKIE values through the stripslashes function.

register_globals
Never rely on this option beeing set. Always access all GET, POST and COOKIE values through the ‘superglobal’ $_GET, $_POST and $_COOKIE variables. For convenience declare $PHP_SELF = $_SERVER['PHP_SELF']; in your global include file after the gpc_magic_quotes quirk.

File uploads:
The maximum size of an uploaded file is determined by the following parameters:
file_uploads must be 1 (default)
memory_limit must be slightly larger than the post_max_size and upload_max_filesize
post_max_size must be large enough
upload_max_filesize must be large enough
Have one single configuration file
You should define all configuration parameters of your application in a single (include) file. This way you can easily exchange this file to reflect settings for your local development site, a test site and the customer’s production environment. Common configuration parameters are:
database connection parameters
email addresses
options
debug and logging output switches
application constants
Keep an eye on the namespace
As PHP does not have a namespace facility like Java packages, you must be very careful when choosing names for your classes and functions.
Avoid functions outside classes whenever possible and feasible. Classes provide some extra namespace for the methods and variables that live inside them.
If you declare global functions use a prefix. Some examples are dao_factory(), db_getConnection(), text_parseDate() etc.
Use a database abstraction layer
In PHP there are no database-independent functions for database access apart from ODBC (which nobody uses on Linux). You should not use the PHP database functions directly because this makes it expensive when the database product changes. Your customer may move from MySQL to Oracle one day or you will need an XML database maybe. You never know. Moreover an abstraction layer can ease development as the PHP database functions are not very userfriendly.
Use Value Objects (VO)
VOs are actually a J2EE pattern. It can easily be implemented in PHP. A value object corresponds directly to a C struct. It’s a class that contains only member variables and no methods other than convenience methods (usually none). A VO corresponds to a business object. A VO typically corresponds directly to a database table. Naming the VO member variables equal to the database fields is a good idea. Do not forget the ID column.
class Person {
var $id, $first_name, $last_name, $email;
}
Use Data Access Objects (DAO)
DAO is actually a J2EE pattern. It can easily be implemented in PHP and helps greatly in separating database access from the rest of your code. The DAOs form a thin layer. The DAO layer can be ‘stacked’ which helps for instance if you want to add DB caching later when tuning your application. You should have one DAO class for every VO class. Naming conventions are a good practice.
class PersonDAO {
var $conn;

function PersonDAO(&$conn) {
$this->conn =& $conn;
}

function save(&$vo) {
if ($v->id == 0) {
$this->insert($vo);
} else {
$this->update($vo);
}
}

function get($id) {
#execute select statement
#create new vo and call getFromResult
#return vo
}

function delete(&$vo) {
#execute delete statement
#set id on vo to 0
}

#– private functions

function getFromResult(&vo, $result) {
#fill vo from the database result set
}

function update(&$vo) {
#execute update statement here
}

function insert(&$vo) {
#generate id (from Oracle sequence or automatically)
#insert record into db
#set id on vo
}
}
A DAO typically implements the following methods:
save: inserts or updates a record
get: fetches a record
delete: removes a record
The DAO may define additional methods as required by your application’s needs. The should only perform actions that require the database (maybe only for performance reasons) and can not be implemented in a different mannor. Examples: isUsed(), getTop($n), find($criteria).

The DAO should only implement basic select / insert / update operations on one table. It must not contain the business logic. For example the PersonDAO should not contain code to send email to a person. For n-to-n relationships create a separate DAO (and even a VO if the relationships has additional properties) for the relation table.

Write a factory function that returns the proper DAO given the class name of a VO.Caching is a good idea here.
function dao_getDAO($vo_class) {
$conn = db_conn(‘default’); #get a connection from the pool
switch ($vo_class) {
case “person”: return new PersonDAO($conn);
case “newsletter”: return new NewsletterDAO($conn);

}
}

Generate code
99% of the code for your VOs and DAOs can be generated automatically from your database schema when you use some naming conventions for your tables and columns. Having a generator script ready saves you time when you are likely to change the database schema during development. I successfully used a perl script to generate my VOs and DAOs for a project. Unfortunately I am not allowed to post it here.
Business logic
Business logic directly reflects the use cases. The business logic deals with VOs, modifies them according to the business requirements and uses DAOs to access the persistence layer. The business logic classes should provide means to retrieve information about errors that occurred.
class NewsletterLogic {
function NewsletterLogic() {
}

function subscribePerson(&$person) {

}

function unsubscribePerson(&$person) {

}

function sendNewsletter(&$newsletter) {

}
}

Page logic (Controller)
When a page is called, the page controller is run before any output is made. The controller’s job is to transform the HTTP request into business objects, then call the approriate logic and prepare the objects used to display the response.
The page logic performs the following steps:

1. The cmd request parameter is evaluated.
2. Based on the action other request parameters are evaluated.
3. Value Objects (or a form object for more complex tasks) are created from the parameters.
4. The objects are validated and the result is stored in an error array.
5. The business logic is called with the Value Objects.
6. Return status (error codes) from the business logic is evaluated.
7. A redirect to another page is executed if necessary.
8. All data needed to display the page is collected and made available to the page as variables of the controller. Do not use global variables.

Note: it is a good idea to have a utility function that returns a parameter that is sent via GET or POST respectivly and provide a default value if the parameter is missing. The page logic is the only non-HTML include file in the actual page! The page logic file must include all other include files used by the logic (see base.inc.php below). Use the require_once PHP command to include non-HTML files.
class PageController {
var $person; #$person is used by the HTML page
var $errs;

function PageController() {
$action = Form::getParameter(‘cmd’);
$this->person = new Person();
$this->errs = array();

if ($action == ‘save’) {
$this->parseForm();
if (!this->validate()) return;

NewsletterLogic::subscribe($this->person);

header(‘Location: confirmation.php’);
exit;
}
}

function parseForm() {
$this->person->name = Form::getParameter(‘name’);
$this->person->birthdate = Util::parseDate(Form::getParameter(‘birthdate’);

}

function validate() {
if ($this->person->name == ”) $this->errs['name'] = FORM_MISSING;
#FORM_MISSING is a constant

return (sizeof($this->errs) == 0);
}
}
Presentation Layer
The top level page will contain the actual HTML code. You may include HTML parts that you reuse across pages like the navigation etc. The page expects the page logic to prepare all business objects that it needs. It’s a good idea to document the business objects needed at the top of the page.
The page accesses properties of those business objects and formats them into HTML.

value=”name); ?>”>

Localization
Localization is a problem. You must choose among
a) duplicating pages
b) removing all hardcoded strings from your HTML.

As I work in a design company I usually take approach a). Approach b) is not feasible as it makes the HTML very hard to read and nearly impossible to edit in a visual web editor like Dreamweaver. Dynamic content is hard enough to edit with Dreamweaver. Removing also all strings, makes the page look quite empty…

So finish the project in one language first. The copy the HTML pages that need translation. Use a naming convention like index_fr.php to designate the French version of the index page. Always use the ISO two letter language codes. Do not invent your own language codes.

To keep track of the language the user selected you must choose among
a) storing the language setting in a session variable or cookie
b) reading the preferred language (locale) from the HTTP headers the browser sends you
c) appending the language to the URL of every link in your application

While a) seems a lot more easier than c) it may be subject to session timeout. Option b) should only be implemented as an extension to a) or c).
Strings in a database must be localized too!
Making your application location independent
PHP has problems in some situations when include files are nested and reside in different folders and it is unclear at which directory level the file will be included. One can solve this by using absolute path names or using $_SERVER['DOCUMENT_ROOT'] as a starting point. However this makes your code location dependent – it will not run anymore if you move it down your directory structure one level. Of cource we do not like that.
I have found a convenient solution to this problem. The toplevel page (the one that is called by the browser) needs to know the relative path to the application root directory. Unfortunately there is no such function in PHP and the webapp context concept is completely absent in PHP. So we can not automatically determine the application root reliably in all situations (It is *really* impossible. Don’t even try. It’s not worth the effort.)
Let’s define a global variable called $ROOT in an include file in every directory that contains toplevel pages. The include file (call it root.inc.php) must be included by the page logic before any other include files. Now you can use the $ROOT variable to reference include files with their exact path!

Sample:
We have toplevel pages in /admin/pages/. The $ROOT variable must therefore be set to $ROOT = ‘../..’;. The page logic included by pages in that folder would reference their include files like require_once(“$ROOT/lib/base.inc.php”);.

In my suggested folder outline (see below) we don’t even need that, since all toplevel pages reside in the webapp root directory anyway. So the webapp root directory is always the current directory.
Folder outline
I suggest you make one file per class and follow a naming convention. Make sure that all your include files end with .php to avoid disclosure of your code to malicious users, which is a major security problem. I suggest the following folder structure:
/ Webapp root directory. Contains the pages that are actually called by the browser.
/lib/ Contains base.inc.php and config.inc.php
/lib/common/ Contains libraries and tools reusable for other projects, like your database abstraction classes.
/lib/model/ Contains the Value Object classes
/lib/dao/ Contains the DAO classes and the DAO factory
/lib/logic/ Contains the business logic classes
/parts/ Contains partial HTML that is included by pages
/control/ Contains the page logic. For larger applications you may want additional sub-directories for the individual parts (e.g. /admin/, /pub/) of your application to make the root directory a little lighter. Each of them would have their own control sub-directory.

Provide a base.inc.php file that includes (require_once) in the right order:
frequently used stuff (database layer) from /lib/common
the config include file
all classes from /lib/model
all classes from /lib/dao

Of course you will have additional directories for your images, uploaded files, … etc.

Access QuickBooks from a Windows Service (such as Apache)

December15

Quickbooks Desktop Integration

I was trying to get a Apache/PHP application to interface with Quickbooks using QBXML through the Quickbooks SDK and kept receiving the following error:

  • “Could not start Quickbooks”

When looking at the SDK log file (Located at C:\Documents and Settings\All Users\Application Data\Intuit\QuickBooks\qbsdklog.txt), I kept seeing the following error when attempting access the file:

20091215.215811 I 3416 RequestProcessor ========= Started Connection =========
20091215.215811 I 3416 RequestProcessor Request Processor, QBXMLRP2 v7.0
20091215.215811 I 3416 RequestProcessor Connection opened by app named ‘php QB via C#’
20091215.215812 E 3416 RequestProcessor QuickBooks is not running and the data file name is not provided. Cannot continue.
20091215.215812 E 3416 RequestProcessor Could not create instance of QuickBooks. hr = 80040416
20091215.215812 I 3416 RequestProcessor Connection closed by app named ‘php QB via C#’
20091215.215812 I 3416 RequestProcessor ========== Ended Connection ==========
========= Started Connection =========
Request Processor, QBXMLRP2 v7.0
Connection opened by app named 'php QB via Apache'
An internal error occured while looking for a running instance of
QuickBooks. Cannot continue. hr = 800401f3
Could not create instance of QuickBooks. hr = 8004041c
Connection closed by app named 'php QB via Apache'
========== Ended Connection ==========

If I ran my integration application through command prompt (via a .bat file) instead of Apache it worked perfectly. But Apache would not sync. I was running the Apache service as an Administrator which is I though all I needed to do. I was wrong.

Apparently accessing QuickBooks from a Windows service requires special COM permissions, because there are in-process/out-of-process Windows COM issues. We basically need to do three things: configure the COM permissions for QBXMLRP2e (installed with the Quickbooks SDK), configure Apache to run as a Windows user and configure Quickbooks to allow the application to connect.

COM Permissions configuration:

  1. Make sure the Quickbooks SDK is installed.
  2. Run command prompt and type: “C:\Program Files\Intuit\IDN\QBSDK7.0\tools\access\QBXMLRP2e\qbXMLRP2e.exe” /RegServerThis adds qbXMLRP2e to the DCOM settings list.
  3. Run “Component Services” from “Control Panel > Administrator Tools”
  4. Then from “Console Root > Computers > My Computers > DCOM Config” right click on “qbXMLRP2e” and select Properties

“Authentication level” should be set to Default

Go in Security “Launch and activation” select “Customize” and press Edit. Add the Windows user the service runs as and assign all calling rights. “Access” select “Customize” and press Edit. Add the Windows user the service runs as and assign all. “Configuration” select “Customize” and press Edit. Add the Windows user the service runs as and assign all.

Go in “Identity” tab and select “The interactive user”

Click Apply.

Apache/Quickbooks configuration:

  1. Make sure Apache is running as the user configured as the DCOM Windows user. Right click on the Apache Monitor and select Open Services. Change the “Log On As” user for the Apache service.
  2. Open Quickbooks and set it to multi-user mode. Be sure to clear out any applications in “Preference | Application Settings” first to make sure the permission settings get set correctly.
  3. Run a GUI version of your application accessing quickbooks. For example running your program through command line instead of Apache.
  4. Follow the prompts in Quickbooks to allow the application access.
  5. Run the integration through Apache and it should work now!

This took forever to figure out and hopefully it helps someone googling the error codes I was receiving.

Chase website missing a feature

December6

I paid off my car in October. My Chase auto loan account is closed. It says “account closed” when I try to login at chase.com. However, during the first week of December, my bank account was charged a regular car payment. Apparently unless you cancel the auto pay feature on their website they keep charging you against your closed account. And then when you call them and ask them about it, they say the only thing you can do is send them a letter because since the account is closed, they have no way of refunding the money. Chase really needs to have a “feature” where they don’t continue auto pay on a closed account that has a $0 balance….

posted under Random | No Comments »

Real People = Real Results!

November22

On December 19th, Kenneth Wade A., is holding a “Citizens for Change” meeting at 215 East 3rd Street, Dayton Ohio (Dayton Metro Library) 10am-12pm. Help us put Dayton back on track!

http://www.ourdayton.com/

Facebook

Community Supported Agriculture

November18

Ohio needs to get a handle on its obesity epidemic: http://www.daytondailynews.com/news/dayton-news/half-of-ohios-adults-may-be-obese-by-2018half-of-ohios-adults-may-be-obese-by-2018-405474.html. Obesity, as they mention in the article, is defined by the federal Centers for Disease Control and Prevention as anyone with a body mass index of 30 or higher. You can calculate your BMI with an online BMI calculator: http://www.nhlbisupport.com/bmi/

One great way to fight obesity in Ohio is to promote community supported agriculture (CSA’s) such as Blue Bird Hills: http://www.bluebirdhills.com/CSA%20Introduction.html. For 5 months out of the year a large box of vegetables is picked up at convenient locations throughout the Dayton area. As much as I like fresh vegetables, I would never buy this much from a store each week, and best of all – it’s all organic. It keeps your fridge well stocked, and scrambling for ways to incorporate fresh food into every meal so you finish it all off before the next box arrives.

This was my first year belonging to a CSA, and I have never eaten healthier. I will definitely be joining next year as well.

There are other CSA’s in the area such as “The Happy Box” from Fulton Farms, http://www.fultonfarmsorganic.com/ which delivers the box right to your door. There are many offerings in our area: http://miamivalleygrown.ning.com/page/2613934:Page:581 and if enough Ohioans start joining CSA’s maybe instead of the obesity rate climbing from 34% to 50.9% it will actually decrease!

Windows 7 Starter Edition

November11

Recently purchased a netbook with Windows 7 on it a few weeks ago. I didn’t realize until I started using it the massive limitations of Windows 7 Starter Edition. This article articulates it pretty well: http://www.eweek.com/c/a/Windows/Microsofts-Windows-7-Could-Disappoint-Netbook-Users-Says-Survey-368762/

Even worse, when the netbook was first loading, they misspelled initializing as “Initailizing…”. My blackberry doesn’t take very good shots of LCD netbook screens, but here is the picture:

Windows 7 misspells initializing

Windows 7 misspells initializing as "Initailizing"

A coworker of mine is always talking about how great Ubutnu is: http://www.ubuntu.com/, and I do have an extra laptop to try it out on. So that seems like a nice weekend project.

Viva la Vida

October24

I used to rule the world
Seas would rise when I gave the word
Now in the morning I sleep alone
Sweep the streets I used to own

I used to roll the dice
Feel the fear in my enemy’s eyes
Listen as the crowd would sing
“Now the old king is dead! Long live the king!”

One minute I held the key
Next the walls were closed on me
And I discovered that my castles stand
Upon pillars of salt and pillars of sand

I hear Jerusalem bells a ringing
Roman Cavalry choirs are singing
Be my mirror, my sword and shield
My missionaries in a foreign field

For some reason I can’t explain
Once you go there was never
Never an honest word
And that was when I ruled the world

It was the wicked and wild wind
Blew down the doors to let me in
Shattered windows and the sound of drums
People couldn’t believe what I’d become

Revolutionaries wait
For my head on a silver plate
Just a puppet on a lonely string
Oh who would ever want to be king?

I hear Jerusalem bells a ringing
Roman Cavalry choirs are singing
Be my mirror, my sword and shield
My missionaries in a foreign field

For some reason I can’t explain
I know Saint Peter won’t call my name
Never an honest word
But that was when I ruled the world

I hear Jerusalem bells a ringing
Roman Cavalry choirs are singing
Be my mirror, my sword and shield
My missionaries in a foreign field

For some reason I can’t explain
I know Saint Peter won’t call my name
Never an honest word
But that was when I ruled the world

posted under Random | No Comments »

IT Security Staff

October20

This was a very interesting article I came across: http://technet.microsoft.com/en-us/library/ee672311.aspx

It was part of Microsofts Security Newsletter, which always seems to have something interesting: http://technet.microsoft.com/en-us/dd162324.aspx

Twitter Updates

October13

Two ways to add Twitter to your site. For the non-tech or easy to do, use Twitter’s built in widgets: http://twitter.com/badges/which_badge

Or using PHP code your own page. Below is the code followed by the results:


Warning: file_get_contents() [function.file-get-contents]: open_basedir restriction in effect. File(/home/content/k/e/l/kellymas/html/includes/include_twitter.php) is not within the allowed path(s): (/var/www/clients/client2/web2/web:/var/www/clients/client2/web2/tmp:/usr/share/php5) in /var/www/clients/client2/web2/web/blog/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 7

Warning: file_get_contents(/home/content/k/e/l/kellymas/html/includes/include_twitter.php) [function.file-get-contents]: failed to open stream: Operation not permitted in /var/www/clients/client2/web2/web/blog/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 7

Warning: include() [function.include]: open_basedir restriction in effect. File(/home/content/k/e/l/kellymas/html/includes/include_twitter.php) is not within the allowed path(s): (/var/www/clients/client2/web2/web:/var/www/clients/client2/web2/tmp:/usr/share/php5) in /var/www/clients/client2/web2/web/blog/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 13

Warning: include(/home/content/k/e/l/kellymas/html/includes/include_twitter.php) [function.include]: failed to open stream: Operation not permitted in /var/www/clients/client2/web2/web/blog/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 13

Warning: include() [function.include]: Failed opening ‘/home/content/k/e/l/kellymas/html/includes/include_twitter.php’ for inclusion (include_path=’.:/usr/share/php:/usr/share/pear’) in /var/www/clients/client2/web2/web/blog/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 13

« Older EntriesNewer Entries »