This chapter presents some tips and tricks for webapp development, including database programming, server-side programming (PHP and others), client-side programming (HTML/CSS/JavaScript); Version Control System (Git); etc.
Some parts are platform specific (e.g., Apache, PHP, Ubuntu only); while many are generic (e.g., Git, database naming convention).
Version Control System
You need to set up a Version Control System, such as Git, for backup, version control and collaboration. Read "How to setup and get started with Git".
Configuring Virtual Hosts in Apache Server
You may need to run multiple web sites under the same Apache server, e.g., production and test systems. It is common to run port-based Virtual Hosts on Apache, e.g., production system running on port 80, while test system on port 8080.
Read "Port-based Virtual Host" on how to configure port-based virtual host on Apache server.
For Ubuntu: Instead of adding the configuration directly into the main configuration file, you create virtual host configuration in "\etc\apache2\sites-available
", run helper utility "a2ensite
" to symlink it to the "\etc\apache2\sites-enabled
", which is then picked up by the main configuration "apache.conf
".
Database Convention
Webapp design typically begins with database design.
Over the years, I have developed my own conventions that greatly improve my productivity in writing webapp.
Naming Convention
- Database name: in uppercase joined with underscore (e.g.,
QUIZ_SYSTEM
) - Table name: in camel-case with initial cap (e.g.,
ProblemPool
) - Column name: in camel-case with initial lowercase (e.g.,
problemNo
,studentID
). Acronyms like ID, OO shall be in uppercase. - ENUM name: Use the exact format to be displayed, e.g., 'NO - Wrong Output', 'Student'
- Procedure name: in camel-case with prefix "
proc
" (e.g.,procProcedureName
) - Function name: in camel-case with prefix "
fn
" (e.g.,fnFunctionName
) - Procedure/Function parameters: in camel-case with prefix "
in
", "out
" or "io
" (e.g.inXxx
,outXxx
,ioXxx
) - Trigger name: in camel-case with prefix "
tg
" (e.g.,tgTriggerName
) - Event name: in camel-case with prefix "
evt
" (e.g.,evtEventName
)
Do not use space (blank) or special characters in names, unless you are looking for more challenges.
Primary Keys
In general, the primary keys shall have suffix of "ID
" (for Identification); or "No
" (for number); or "code
", e.g., userID
, problemNo
, tutorialNo
, courseCode
, groupCode
.
Do not simply use "ID
" to name the primary key of all tables (although it is theoretically correct). You will get very confuse in your PHP/JavaScript programming codes. Use "ID
" and "code
" for string
type (e.g. userID
, courseCode
); "No
" for int
type (e.g. submissionNo
, tutorialNo
, problemNo
).
Validation Regex
We should also define validation regex for indexing columns during the database design. These regex will then be used consistently in programming codes (such as PHP/JavaScript) to check the input validity and to present attack, e.g.,
loginID
:/^[\w-]{4,20}$/
(4 to 20 word characters of[A-Za-z0-9_]
and dash (-
))courseCode
:/^[\w-]{3,15}$/
(3 to 15 word characters of[A-Za-z0-9_]
and dash (-
))password
:/^\w{4,}$/
(4 or more word characters of[A-Za-z0-9_]
)int
(problemNo
,SubmissionNo
, etc.):/^\d+$/
(1 or more digit characters of[0-9]
)
Ownership and Permission for Apache (Unix - Ubuntu/Mac OS X)
In Unix, Apache parent process is started by the superuser "root
"; while the child processes are run by a special non-interactive user called "www-data
". The user www-data
is in group www-data
only.
Requirements
- The user
www-data
needs read access to public HTML/CSS/JavaScript and server-side scripts (such as PHP). It needs write access to directories such as "log
" and "work
". - A group of developers (says Peter and Paul) need read/write access to all resources.
- Others shall have no permission at all to all resources.
Create Users and Group
To meet the requirements, we shall create a group called web-dev
and places all developers (says users peter
and paul
) inside the group.
// create users "peter" and "paul" $ sudo adduser peter $ sudo adduser paul // show user info $ less /etc/passwd // create group "web-dev" (for webapp developers) $ sudo addgroup web-dev // show groups $ less /etc/group // add users "peter" and "paul" to group "web-dev" // -a for append, -G for groupname $ sudo usermod -a -G web-dev peter $ sudo usermod -a -G web-dev paul // If desired, also add to "sudo" group
File/Directory Ownership and Permissions
All directories/files are to be owned by user www-data
and group web-dev
.
For public HTML/CSS/JavaScript and server-side Scripts (e.g. /var/www
):
- Directory/Sub-directories: user
www-data
needsr-x
(list, access), groupweb-dev
needsrwx
(list, write and access for maintenance), setgid to inherit group ownership, i.e. mode 2570 (dr-xrws---
). - Files: user
www-data
needsr--
(read-only), groupweb-dev
needsrw-
(read-write for maintenance), i.e., mode 0460 (-r--rw----
). (Note: No "x" needed for server-side scripts such as PHP.)
$ cd /var/www // Change ownership to user www-data and group web-dev // -R for recursive $ sudo chown -R www-data:web-dev . // Change permissions of directory and sub-directories // -type d for directory // 2 = setgid // 570 = r-xrwx--- $ sudo find . -type d -exec chmod 2570 {} + // Change permissions of files // -type f for file // 460 = r--rw---- $ sudo find . -type f -exec chmod 0460 {} + // Check $ ls -al // Repeat for all such directories
For "work
" and "log
" files: read-write and create.
- Directory and sub-directories: user
www-data
needsrwx
(list, write and access), groupweb-dev
needsrwx
(list, write and access), setgid to inherit group ownership, i.e. mode 2770 (drwxrws---
). - Files: user
www-data
needsrw-
(read-write), groupweb-dev
needsrw-
(read-write for maintenance), i.e., mode 0660 (-rw-rw----
).
$ cd /path-to/work // Change ownership to user www-data and group web-dev // -R for recursive $ sudo chown -R www-data:web-dev . // Change permissions of directory and sub-directories // -type d for directory // 2 = setgid // 770 = rwxrwx--- $ sudo find . -type d -exec chmod 2770 {} + // Change permissions of files // -type f for file // 660 = r--rw---- $ sudo find . -type f -exec chmod 0660 {} + // Check $ ls -al // Repeat for all such directories
Webapp Directory Organization and File Conventions
Directory Organization
An example of webapp directory structure of PHP webapp is as follows:
app-base | +-- www (read-only): public www (HTML/CSS/JavaScript + Server-side scripts) | | | +-- assets (read-only) | | | +-- include: Include files such as Header.inc.php, Footer.inc.php, Init.inc.php. | | Include files are named as ".inc.php." | +-- css: | +-- js: Do not named as "javascript", which causes conflict in some situation | +-- images: Images used by HTML, e.g., favicon.ico | | | +-- upload (read-write): data/images uploaded by users, but need to be placed under public www | | (These directories are outside the public www for security) +-- php-lib (read-only): additional PHP libraries in sub-directories (such as kLogger) +-- conf (read-only): Confidential configuration files, e.g., password +-- export (read-write): data (report) exported by the app, may contain sensitive info +-- work (read-write): generated work files +-- log (read-write): log file, may contains sub-directories for each type of logs, | such as php-log, java-log | | (These are not used by the app in operation) +-- sql: SQL scripts | +-- init | +-- maintenance +-- test: testing scripts (e.g., Selenium acceptance test scripts) +-- import: description files, used to populate the database +-- doc: public documentations (e.g., user guide in Wiki) +-- private: not too be shared or tracked by git. +-- doc: private documentations +-- test: private test scripts +-- data: confidential data
Each directory shall preferably contain a README.txt file to explain the contents of the directory.
Filename Conventions
- Filename: in camel-case with initial cap (e.g.,
ForgotPassword.php
,SetupProblems.php
) - PHP Include Filename: *.inc.php (e.g.,
Header.inc.php
,UserFunctions.inc.php
)
PHP Logging - kLogger
HERE.
PHP Webapp Examples
[TODO]
jQuery
jQuery (@ http://jquery.com) is a JavaScript library that provides cross-browser support. Some JavaScript functions are not supported in certain browser.
The "query" refers to querying DOM elements within the HTML document.
[TODO]
BootStrap
BootStrap (@ http://getbootstrap.com) is set of CSS and JavaScript (jQuery) that greatly simplifies the client-side development. You can get a professional looking front-end in little time.
Setting up
- Download BootStrap from http://getbootstrap.com/getting-started/#download.
- BootStrap requires jQuery. Download jQuery from http://jquery.com.
- Move the CSS files to sub-directory "
css
" and JavaScript files to "js
". - Include the BootStrap's CSS, BootStrap and jQuery JavaScript File in your HTML document as follows:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Bootstrap viewport setup --> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap's CSS --> <link href="css/bootstrap.min.css" rel="stylesheet"> <title>Your Title</title> </head> <body> <h1>Hello, world!</h1> <!-- Place the JavaScript at the end so the the page loads faster --> <!-- jQuery is needed for BootStrap --> <script src="js/jquery.min.js"></script> <!-- Include BootStrap's all compiled plugins --> <script src="js/bootstrap.min.js"></script> </body> </html>
Getting Started
Read:
- BootStrap CSS @ http://getbootstrap.com/css.
- Study the bootstrap examples @ http://getbootstrap.com/getting-started/#examples. "View Source" as well as using Firebug to study the HTML/CSS.
Some Basic Usage Notes:
- Place your body content under a
<div class="container">
or<div class="container-fluid">
(for full screen width). - BootStrap organizes the containers in rows. Each row has 12 columns.
- [TODO] more
Tips ad Tricks
Login Page <form>'s width
To control the width of the login form (placed under a "container"):
<div class="container">
<form method="post" style="max-width:500px; margin:0 auto;">
......
</form>
</div>
See http://getbootstrap.com/examples/signin.
<body> Tag's padding-top to skip the navigation bar
If you use a fixed navigation bar (navbar-fixed-top
) at the top of the page, which has a "position:fixed
" attribute, you need to use padding-top
to skip the navigation bar, e.g.,
body {
padding-top: 50px; /* To skip the "fixed" navigation bar */
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
}
Collapsable Navigation Bar
When the screen width is too narrow to show all the menu items, a "collapse" button shown up, which contains the menu items.
<nav class="navbar navbar-default"> <div class="navbar-header"> <!-- Place "collapse button" and "brand" here --> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-menu"> <span class="sr-only">Toggle navigation</span> <!-- to draw 3 bars on the collapse button --> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">LOGO</a> </div> <!-- Collect all the collapsable items here, with "id" targeted by the above button --> <div class="collapse navbar-collapse" id="navbar-collapse-menu"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Item 1</a></li> <li><a href="#">Item 2</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> Item 3<b class="caret"></b> </a> <ul class="dropdown-menu"> <li><a href="#">Item 3-1</a></li> <li><a href="#">Item 3-2</a></li> <li class="divider"></li> <li><a href="#">Item 3-3</a></li> </ul> </li> </ul> </div> </nav>
See http://getbootstrap.com/components/#navbar.
Parsley JS for Client-side Input Validation
Parsley JS (@ http://parsleyjs.org/) provides a JavaScript form validation library.
Setting Up
[TODO]
Example (with BootStrap)
<form method="post" data-parsley-validate> <!-- Enable Parsley for form input validation --> <div class="form_group"> <label for="loginID">Login ID</label> <input type="text" name="loginID" id="loginID" class="form-control" autofocus required data-parsley-trigger="change" pattern="/^[\w-]{4,20}$/" data-parsley-pattern-message="Invalid Login ID" /> </div> <div class="form-group"> <label class="control-label">Email</label> <input class="form-control" name="email" id="email" type="email" required data-parsley-trigger="change" /> </div> ...... </form>
Setup HTTPS for Apache
For Windows: HERE.
For Ubuntu: HERE.
Profiling PHP Webapps with XDebug and WebGrind
The XDebug PHP extension helps you debugging your script by providing a lot of valuable debug information and generate trace and profiling information.
Webgrind (@ http://code.google.com/p/webgrind) is an XDebug profiling web front end in PHP5. It implements a subset of the features of kcachegrind and installs in seconds and works on all platforms.
[TODO]