logo sykohpath.com

				where code goes to die


aexivile (1)
anime (3)
april 1st (1)
arr (1)
auto install (1)
auto mount (1)
bible (1)
black rock shooter (1)
blog (9)
burg (1)
c++ (1)
cat (4)
chinese (1)
code (1)
complaining (4)
conference (1)
costume (1)
create (1)
crossfit (1)
crud (2)
database (1)
delete (1)
diet (1)
dog (2)
dual boot (1)
game (1)
git (3)
github (3)
graphics (1)
gt780 (1)
guild wars (2)
health (3)
healthcare (1)
heptadecafelinagon (1)
include (1)
indie (2)
japanese (5)
kanji (1)
link (10)
linux (5)
login background (1)
maintenance (1)
mal (1)
math (1)
maze generation (1)
mental disorder (1)
mint (4)
mint 12 (4)
msi (1)
music video (1)
mvc (1)
myanimelist (1)
mysql (1)
path (1)
pax09 (6)
pest control (8)
philosophy (1)
php (14)
pilates (1)
pipa (1)
pirates (1)
ptsd (1)
reference (2)
review (2)
sad (1)
sbeu (1)
script (1)
security (1)
site (3)
sitemap (1)
snowboard (2)
sopa (1)
sss (2)
steam (1)
story (8)
sublime text 2 (1)
super bugman extreme ultra (1)
tags (1)
terminal (1)
terminix (8)
test (3)
time change (1)
todo (1)
tutorial (8)
tv (1)
ubuntu (1)
unix (1)
update (2)
updates (1)
what (1)
windows 7 (2)
xml (1)
youtube (1)
試験 (1)


Released a game on Steam.

I "did" it.  I made a game and just released it as Early Access on Steam.

Total Development time so far: 8 years

First day sales: 0  


super bugman extreme ultra, sbeu, steam, indie,


The Irony

Oh, the irony of having a website made in 2009 look and work better than a website made in 2017...

Lovin' it.  My New Year's resolution is to promise to update the website but then immediately forget about it for the next 3 years.



Is this thing still on?

uh yeah.  Been a couple years, hasn't it?

Guess I should make this site responsive and up to date.

test, what,


The first time I encountered the word "Pilates," I thought it was a bastardization of the word "Pirates," but it could have been a simple misprint in the gym program offering.  The picture was enticing as well, with a picture of a woman in a rowing position.  They have to advertise to the female crowd, right?  What's worse than a room full of sweaty men rowing a boat?

When I arrived that first night, I was surprised at how well their advertisement had worked; they entire room was full of women!   Yes, plenty of booty for the plunderin'!  They unrolled seafoam-green mats on the floor, which I assumed helped the atmosphere.  I followed in fashion, an noticed a few odd glances from my fellow mateys.  I was asked, "Why the puffy shirt?"

I snickered, and replied, "Just trying to get into the spirit of the class!  I'm not going to row in this."  I removed it to show my striped moisture-wicking shirt.  I was lucky enough to find a red-white one after stopping off for some groceries after my normal gym routine the previous night.

The instant I spotted the teacher enter the class, I trotted up to her and exclaimed loudly, "Aye Cap'n! Where we be sailin' off to ta-night!?"

The look she gave me pierced my soul.  After she explained what "Pilates" was, I removed my eye-patch and slowly walked out in shame.  Out of humiliation, I cancelled my gym membership on the way out.

arr, pirates, pilates,


"Often experts do arrive at correct answers but it is the phenomenon of uninformed people thinking they know better that creates 'controversies' where there shouldn't be any." - some random guy on Reddit



Line Endings

/bin/sh: /usr/local/sbin/<filename>.php: /usr/local/bin/php^M: bad interpreter: No such file or directory

I have a php script that is run as a cron job.  I get this error when I try to run it from the command line, and this is due to line endings being saved as Windows (CRLF) instead of plain old Unix (LF).  A few methods to fix this, however, they weren't quite working - reg ex searches "completed" but didn't change them.

1) vi <filename>.php
2) :set fileformat=unix
3) :w

Magic.  This forces the file to be saved as Unix, and changes all the line endings to match.  :x and test...


No error, so cron job will work fine now!



no I didn't forget

Suddenly got super busy with life, so haven't had time to continue web dev tutorial on daily basis - it's coming!  be patient!



Set up a cron job

A cron job is a way to "automatically" run a script at a specific interval.  It's not too difficult to setup.

1) From terminal, edit the cron table:
crontab -e
2) That starts a VI session to enter lines.  Press i to start edit mode.
3) Add the job in the following format:
<minute>     <hour>     <month_day>     <month>     <week_day>     [removed]
So, an example to run a script every weekday (1-5) at midnight (0:0), every month (*):
0    0    *    *    1-5    /home/sykohpath/scripts/script.sh
4) When finished, press ESC, then : to bring up the prompt, and finally save and quit by typing x

Note: An email is sent to the useracount that executes the cron job, so it's a great way to get confirmation that the script ran automatically or not.  However, this can get annoying, so to disable it, add the following at the end of the cron job line that emails are not desired:
/dev/null 2>&1
(write output to null).

Reference: Crontab Quick Reference

linux, reference,


Web Development From Scratch, Day 8

After "Create", there is "Read".  Technically we covered part of this yesterday with "get_all_users" in our model, but we need to actually use it - mainly for a login form.  It's quite simple, really.  Use SQL statements to Read from the database.

First, we'll have to actually create the login form.  Nothing too fancy, just a simple "Username" and "Password" setup.

Normally, in an OOP environment, we'd use the same controller for this, but again, since that's a bit later, we're going to create a new controller, called "login.php".  In order to keep things from getting messy, we need to rename our "users.php" to "register.php".  Why didn't we do this earler? oops.  Shoulda planned for that, right?

1) rename controller/users.php to controller/register.php

We need to slightly modify our index.php as well.  Since we access our site by typing in /index.php, and we have that require our controller, how do we make it access our other controller?  /index.php/register would be a rather nice way to do it, right?

There...sort of is...a way to do it without using an actual framework.  We're going to have "ugly" URL's as a result, but there's ways around that, although this is actually beneficial from an SEO standpoint!

1) grab the full URL that was sent: http://<yoursite.com>/index.php/register
2) Parse that: [host]<yoursite.com>, [path]/index.php/register
3) break that up: [0]=> [1]=>index.php [2]=>register
4) If [2] is set, call that "controller", else default to the "index" controller.

Code Sample:
  1. <?php
  2. require "../../includes/db_connect.php"; //modify to your needs
  3. $url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  4. $urlParse = parse_url($url);
  5. $urlParts = explode('/', $urlParse['path']);
  6. if(isset($urlParts[3])){
  7.   //check if file exists
  8.   $controller = "controller/" . $urlParts[3] . ".php";
  9.   if(!file_exists($controller)){
  10.    die("Controller not found: " . $controller);
  11.   }
  12. } else {
  13.   $controller = "controller/index.php";
  14. }
  15. require $controller; //pass to controller
  16. ?>

* Your initial database require should not change, if it's working from last time, don't change it to match what I have!
* We use $_SERVER superglobals to craft the full URL.  Assume we're hitting it from http (we should test for https, honestly)
* Parse_url gives us an array of the url...
* ...and we explode that array into the parts.
* I use the third position, since I'm hitting this from http://sykohpath.com/websitetest.com.git/index.php/register ([1]=>websitetest.com.git [2]=>index.php [3]=>register).  If you're at http://<yoursite.com>/index.php, you'll be using position 2 to grab the controller name ([1]=>index.php, [2]=>/<controller>)
* Need to test if that file even exists! If not, error out.

Since we set a default controller to "index.php", we want this page to be the first page that pulls up, and not directly to our register page.  So, let's whip up the following pages:

Code Sample:
  1. <?php
  2. if($urlParts[2] == "" || $urlParts[3] == ""){
  3.   //index.php was not supplied, so modify links to reflect that
  4.   $prefix = "index.php/";
  5. }
  6. require "view/index.php";
  7. ?>

Code Sample:
  1. <?php
  2. if(!isset($prefix)){ $prefix = ""; } //init
  3. ?><html>
  4. <head>
  5. <title>User Management System</title>
  6. </head>
  7. <body>
  8. <?php if($_SESSION["logged_in"]=="YES"): ?>
  9.   Welcome Back, <?php echo $_SESSION['name']; ?>!<br>
  10.   <a href="<?php echo $prefix; ?>logout">Logout</a><br>
  11. <?php else: ?>
  12.   <a href="<?php echo $prefix; ?>login">Login</a><br>
  13.   <a href="<?php echo $prefix; ?>register">Register</a><br>
  14. <?php endif; ?>
  15. <?php print_r($_SESSION); ?>
  16. </body>
  17. </html>

* Since this is a landing page which can be accessed through index.php via http://<yoursite.com>/ - we need to make sure the links are flexible to handle  http://<yoursite.com>/ as well as http://<yoursite.com>/index.php - we do this by checking our url parts that were set in our index.php for the controller checker.
* We have a check to see if the user is logged in.  If they are, show their name, and a link to logout.  If they aren't, show a link to login.
* For testing/learning purposes, a print_r($_SESSION) displays all that was set once the user is logged in. It's an unneccessary line.

After making those changes, try them out.  Go to index.php/test, as well as index.php/register - test should error, while register should pull up the registration form we made yesterday.  Try index.php/ and the new index page we made should pull up.

One last thing, change the form location in view/user_form.php to point to the new location:
<form name="user_registration" action="register" method="post">

Now to move on to the login form!  Not much to the view:

Code Sample:
  1. <html>
  2. <head>
  3. <title>User Login Form</title>
  4. </head>
  5. <body>
  6. <form name="user_login" action="login" method="post">
  7.   <table>
  8.    <tr>
  9.     <td>
  10.      Username
  11.     </td>
  12.     <td>
  13.      <input type="text" name="username">
  14.     </td>
  15.    </tr>
  16.    <tr>
  17.     <td>
  18.      Password
  19.     </td>
  20.     <td>
  21.      <input type="password" name="password">
  22.     </td>
  23.    </tr>
  24.    <tr>
  25.     <td>
  26.      Errors:
  27.     </td>
  28.     <td>
  29.      <b  echo $errors; ?></b>
  30.     </td>
  31.    </tr>
  32.    <tr>
  33.     <td>
  34.     </td>
  35.     <td>
  36.      <input type="submit" name="submit" value="Submit Form">
  37.     </td>
  38.    </tr>
  39.   </table>
  40. </form>
  41. </body>
  42. </html>

Next our controller, a little bit more complicated:

Code Sample:
  1. <?php
  2. require "model/user_model.php";
  3. //check if logged in!
  4. if($_SESSION['logged_in'] == "YES"){
  5.   require "view/index.php";
  6.   exit;
  7. }
  8. //login user
  9. if(isset($_POST['submit'])){
  10.   //setup array to match table fields=>form values
  11.   $form = array(
  12.    "username" => filter_var($_POST['username'], FILTER_SANITIZE_STRING),
  13.    "password" => filter_var($_POST['password'], FILTER_SANITIZE_STRING)
  14.    );
  15.   //secure/encrypt the password
  16.   $salt = "pepper";
  17.   $form['password'] = md5($salt + md5($form['password']));
  18.   //check if user and password matches
  19.   $errors = validate_user($websitetest_db, $form);
  20.   if(is_array($errors)){
  21.    //if so, cram that user into session
  22.    $_SESSION['logged_in'] = "YES";
  23.    $_SESSION['username'] = $errors['username'];
  24.    $_SESSION['name'] = $errors['name'];
  25.    require "view/index.php";
  26.    exit;
  27.   }
  28. }
  29. require "view/user_login.php";
  30. ?>

* This is VERY similar to the register.php controller.  We still sanitize our fields, and then encrypt the password.
* We pass our associative array (containing the username and password) to the validate_user function, which resides in the model.
* We are passed back an array if the user is validated, and we set the fields according to what was passed back - in this case, only set the flag that the user is logged_in, as well as the username and their name.
* On completion, call the index view, which contains a detection for if the user is logged in.
* If we are not passed back an array, it's an error, so simply call the login view again, which displays the error.

We add the following function to the model:

Code Sample:
  1. ...
  2. function validate_user($db_conn, $dataArray){
  3.   $sql = "SELECT * FROM users WHERE username='" . $dataArray['username'] . "'";
  4.   $result = mysqli_query($db_conn, $sql);
  5.   if(!$result){
  6.    die('SQL Error: ' . mysqli_error($db_conn));
  7.   }
  8.   if(mysqli_num_rows($result) > 0){
  9.    $row = mysqli_fetch_assoc($result);
  10.    if($row['password']==$dataArray['password']){
  11.     return $row; //pass user account back
  12.    } else {
  13.     return "Incorrect Password";
  14.    }
  15.   } else {
  16.    return "Username not found";
  17.   }
  18. }
  19. ...

* Ok, "finally" we deal with the "Read" part of CRUD.
* Using SQL, just pull all records that match the passed username. (Note that we haven't put in a duplicate username filter in our registration yet!)
* If there's more than 0 rows returned, it means that a matching record was found.  Otherwise, 0 rows returned means there were no records that match the passed username.
* Compare the pulled password with the passed password - either it matches, or it doesn't.

After all this, we'll have the following in our $_SESSION:
Array ( [logged_in] => YES [username] => testAccount [name] => Test Account )

Now, if we try to login as "test" (Bob Bobkins!) we'll see that it doesn't work...because the password it's checking against is unencrypted.  We'll deal with that one record in a later section (Delete!).

Our login system is almost complete.  We just need to "logout".  Painfully simple!

Code Sample:
  1. <?php
  2. //check if logged in!
  3. if($_SESSION['logged_in'] == "YES"){
  4.   unset($_SESSION);
  5. }
  6. require "view/index.php";
  7. ?>

GOOD NESS.  That was quite a bit for "Read", wasn't it?  Well, a login system is the...I don't want to say "backbone"...it's more like the "kidneys" of a user management system.

Next will be the user "Profile" once they are logged in.  This will cover "Update" part of CRUD.

php, tutorial, crud, update,


Web Development From Scratch, Day 7

Next up: Create Record in Database.

So the first thing is, to cram our database stuff into the model, make a view, and then tweak the controller to call both of them.  All we'll do for the view is make a simple form, which will tell our php script to create a record with the data entered.  We'll also create a second view which displays all the users in the database.  EASY STUFF, I promise.

Code Sample:
  1. <html>
  2. <head>
  3. <title>User Registration Form</title>
  4. </head>
  5. <body>
  6. <form name="user_registration" action="index.php" method="post">
  7.   <table>
  8.    <tr>
  9.     <td>
  10.      Username
  11.     </td>
  12.     <td>
  13.      <input type="text" name="username">
  14.     </td>
  15.    </tr>
  16.    <tr>
  17.     <td>
  18.      Password
  19.     </td>
  20.     <td>
  21.      <input type="password" name="password">
  22.     </td>
  23.    </tr>
  24.    <tr>
  25.     <td>
  26.      Password Confirm
  27.     </td>
  28.     <td>
  29.      <input type="password" name="password_confirm">
  30.     </td>
  31.    </tr>
  32.    <tr>
  33.     <td>
  34.      Email Address
  35.     </td>
  36.     <td>
  37.      <input type="text" name="emailaddr">
  38.     </td>
  39.    </tr>
  40.    <tr>
  41.     <td>
  42.      Full Name
  43.     </td>
  44.     <td>
  45.      <input type="text" name="fullname">
  46.     </td>
  47.    </tr>
  48.    <tr>
  49.     <td>
  50.      Errors:
  51.     </td>
  52.     <td>
  53.      <b  echo $errors; ?></b>
  54.     </td>
  55.    </tr>
  56.    <tr>
  57.     <td>
  58.     </td>
  59.     <td>
  60.      <input type="submit" name="submit" value="Submit Form">
  61.     </td>
  62.    </tr>
  63.   </table>
  64. </form>
  65. </body>
  66. </html>

* This .php file is almost all HTML.  Note that there are no opening and closing PHP tags.
* However, there is PHP in the "errors" area, which displays the contents of the "$errors" variable.
* Make sure to "git add view/user_form.php" so that it will be properly tracked.

Ok, back to our controller, which we need to "fix":

Code Sample:
  1. <?php
  2. require "model/user_model.php";
  3. if(isset($_POST['submit'])){
  4.   //setup array to match table fields=>form values
  5.   $form = array(
  6.    "username" => filter_var($_POST['username'], FILTER_SANITIZE_STRING),
  7.    "password" => filter_var($_POST['password'], FILTER_SANITIZE_STRING),
  8.    "email_address" => filter_var($_POST['emailaddr'], FILTER_SANITIZE_EMAIL),
  9.    "name" => filter_var($_POST['fullname'], FILTER_SANITIZE_STRING),
  10.    "created" => time(),
  11.    "modified" => time()
  12.    );
  13.   //validate our values
  14.   $errors = "";
  15.   if($form['password'] != $_POST['password_confirm']){
  16.    $errors .= "Passwords do not match!<br>";
  17.   }
  18.   if($form['email_address'] != $_POST['emailaddr']){
  19.    $errors .= "Email Address contains invalid characters!<br>";
  20.   }
  21.   //secure/encrypt the password
  22.   $salt = "pepper";
  23.   $form['password'] = md5($salt + md5($form['password']));
  24.   if($errors == ""){
  25.    //add user to the database
  26.    add_user($websitetest_db, $form);
  27.    //get all users from database
  28.    $allUsers = get_all_users($websitetest_db);
  29.    //show all users
  30.    require "view/user_display.php";
  31.   } else {
  32.    //errors found, show form again
  33.    require "view/user_form.php";
  34.   }
  35. } else {
  36.    //show the user registration form
  37.   require "view/user_form.php";
  38. }
  39. ?>

Wow, what the heck happened here!?
* Start off by including our model.
* Check to see if form was submitted. If not, show the user form, otherwise:
1) set up associative array to store form values.
2) use "filter_var" to sanitize the input. It is a VERY BAD IDEA to not sanitize input, since that opens up possibility of SQL injection, among other issues. (see Security below)
3) Check sanitized input with pure fields for email address. Error if fail.
4) Make sure Password matches the Password Confirm. Error if fail.
5) Hash the password with a very simple salting routine.  Salt is hardcoded, added to the password, and then an MD5 algorithm is applied.  See Security below.
6) if there are no errors, add user to the database, get a list of all users, and show the "show all users" view.
7) if there are errors, show the user registration form again, and pass the errors to it to be displayed.

Consider this statement:
$sql = "SELECT * FROM users WHERE name = '" . $_POST['username'] . "'";

As long as a user enters any name into the field, this sql statement works as intended.  However, the world is filled with assholes, so more than likely, someone will attempt sql injection.  All they have to do is enter something like:


Or something more creative, which will end up with the following full sql statement:


Just like that, your entire users table is gone.  Forever.  That's bad.

I mentioned before that storing passwords in plaintext is a bad idea.  Consider the previous SQL Injection.  A simple "'; SELECT password FROM users;" will then show all the passwords, plain as day, to the intruder. BAD.

So, we need to encrypt the password.  MD5 is NOT an encryption algorithm.  All it does is hash what is supplied.  I won't go into detail here, but consider the following:

'word' -> MD5 -> 'c47d187067c6cf953245f128b5fde62a'

So far so good, right?  MD5 can be reversed, however:

'c47d187067c6cf953245f128b5fde62a' -> MD5 -> 'word'

By adding a salt to the password, we introduce a layer of security:

'word' -> MD5 -> 'c47d187067c6cf953245f128b5fde62a' -> 'saltc47d187067c6cf953245f128b5fde62a' -> MD5 -> '758e43ddf53438199bb0f8aa82a604f7'

But wait, reversing that gives us the salt + md5!  Yeah, but try reversing 'saltc47d187067c6cf953245f128b5fde62a'...it fails.  Mainly because hashes are in Hex, 0-9A-F.  If you make your salt into hex as well, it'll really screw up the reverse process.

It's a complex area, so don't worry if you don't understand it.  Just be aware that you need to use some type of encryption for passwords

To see it in action, load up your database browser and browse the records.  Compare the password fields of "Bob Bobkins" vs one of the records put in by our new form (once you're done putting all the files up!).  Poor Bob's password is sitting there in plaintext.  Our encrypted passwords tower over his exposed weakness.  MD5 stands for MAGIC-DOMINATING-...uhh...FIVE.



Ok, what files do we have left?

Ah, the model.

Our old controller from yesterday has been crammed into a get-all-users function for this one:

Code Sample:
  1. <?php
  2. function get_all_users($db_conn){
  3.    $sql = "SELECT * FROM users";
  4.   $result = mysqli_query($db_conn, $sql);
  5.   if(!$result){
  6.    die('SQL Error: ' . mysqli_error($db_conn));
  7.   }
  8.   $userList = array();
  9.   while($row = mysqli_fetch_assoc($result)){
  10.    $userList[] = $row['name']; //store all names into the list
  11.   }
  12.   mysqli_free_result($result);
  13.   return $userList;
  14. }
  15. function add_user($db_conn, $dataArray){
  16.   $insert_fields = implode(",", array_keys($dataArray));
  17.   $insert_values = "'" . implode("', '", $dataArray) . "'";
  18.   $sql = "INSERT INTO users (" . $insert_fields . ") VALUES (" . $insert_values . ")";
  19.   $result = mysqli_query($db_conn, $sql);
  20.   if(!$result){
  21.    die('SQL Error: ' . mysqli_error($db_conn));
  22.   }
  23. }
  24. ?>

* Not much has changed for the "get_all_users" function.  Notice that we need to pass our database connection to the function in order for the database routines to work correctly.  This is normal.
* New function: add_user.  This takes an associative array, crafts it into a sql statement, and then runs the statement (errors if there is something wrong).

The "sql statement crafter" is a bit...odd...I admit. Normally, prepared statements are ideal with set fields - all we would pass is the data.  However, we're not to the OOP point yet, so we have to deal with procedural goofyness.  I mean, an alternative could be:

$sql = "INSERT INTO users (username, password...) VALUES ('" . $dataArray['username'] . "', '" . $dataArray['password'] . "',...)";

I don't like that.  Sure, everything could be crammed into one line that way, but I like to show off my leet programming skills (stop laughing).  We use the "implode" function to turn the array into a comma-separated string.  Note that the values have to be enclosed in single-quotes.


Whew, tiring.  Let's cover the last, and easiest file yet...display all users view:

Code Sample:
  1. <html>
  2. <head>
  3. <title>Users - Display all</title>
  4. </head>
  5. <body>
  6. <ul>
  7. <?php foreach($allUsers as $value): ?>
  8.   <li><?php echo $value; ?></li>
  9. <?php endforeach; ?>
  10. </ul>
  11. </body>
  12. </html>

* Again, no php tags since this is HTML for the most part.
* We use a foreach to go through the list of all users (Array) and output them in an unordered list.

And hey, that wraps up "create"

Tomorrow, we'll have users be able to log-in.  We MIGHT do "update" and "delete", but only if you're good.

php, tutorial, crud, create,


Web Development From Scratch, Day 6

The typical approach to handling databases is called, "CRUD".  This stands for Create, Read, Update, and Delete.  While not all of these will be used for every project, "Read" is mainly used (speaking from experience, so milage may vary).

Creating the database and table has already been covered.  In the MVC tutorial, there was a quick bit about connecting to the database, but I'll go into further detail here.

1) FIRST. Security.

Do "NOT" have your connection settings in a web-accessible directory (such as http://<yoursite.com>/db_connect.php).  Even though everything is "hidden" in hardcoded php ($username="Database";), there are ways these files can be read.  Even something as simple as accidentally setting PHP files to be "off" will cause .php files to display as HTML instead of being compiled first.

So, on your website directory, you have the following setup:
|- /www
|- /includes

/www is the base folder that is loaded when http://<yoursite.com>/ is accessed.  We want to put our db_connect.php file into /includes, and then call it from our web folder.

There's a couple options here - in "php.ini" (if you can access it), you can specify include directories:
* Adjust "include_path" to include that directory. include_path = "<path_to_your_home>/includes" (don't erase what's already in there!  Also, restart the webserver after changing).

If you're unable to edit the php.ini, hope isn't lost:
* In the php script you can use: set_include_path() - although if anyone is able to see this, they now know the direct route to your included file &#40;not that they'd stumble across it anyway if they are able to poke around the files/directories at this point&#41;
* You can alternatively use __DIR__ as include(__DIR__ . '/includes/file.php');
* Alternative to __DIR__: set_include_path(get_include_path() . '/includes');

On your local computer, instead of putting this into "~/Projects/www.websitetest.com/includes", instead, create this file in a new folder in your Projects directory: "~/Projects/includes"


So, with that setup, make "db_connect.php" in your /includes directory:

Code Sample:
  1. <?php $mysql_db = mysqli_connect('<SERVER>', '<USERNAME>', '<PASSWORD>', '<DATABASE_NAME>');
  2. if(!$mysql_db){
  3. die('Unable to connect to Server: ' . mysqli_connect_error() . ' (#' . mysqli_connect_errno() . ')');
  4. }
  5. ?>

* Use "mysqli" instead of "mysql". This is anotehr security issue, plus, future versions (heck, it's already deprecated in current version) will likely remove "mysql" commands.
* This is using procedural over Object oriented style (OOP).  You'll find out the difference, and use OOP in the future, but for now, procedural is fine as you start out.
* mysqli_connect: Use the details from your webhost.  The last option, <DATABASE_NAME>, refers to the database name, which if you followed these tutorials so far, should be "test".  EASY!
* The only variable carried over from our include is "$mysql_db".  This is a link to the database that will be passed into other functions.
* Connection information (server, username, password) is sometimes stored in variables, and then called after the include/require. This is fine (and used commonly in frameworks).  However, "a hacker" could put "echo $server,$user,$password;" at the top of a file, and then BOOM info is exposed, even though they can't access your /includes directory.  It happens, but meh, keep it in mind - no need to be too paranoid about it.
* "die" causes the script to terminate immediately - here, it will show the connection error and error number.  This is fine for testing, but for a "live" environment, it's better to redirect to a "Server is down" page, and not show any error information.

Hey, guess what?  If you FTP to your site, chances are, it'll plop you in your web root directory.  So how the heck do we create the includes directory (and our file!) outside of webroot?

1) Git Bash, and ssh to your site ("ssh <username>@<yoursite.com>")
2) You should be in your home directory, if not: cd ~
3) "ls" and you should see your /www, as well as other miscellaneous folders.
4) Create the /includes folder: mkdir includes
5) cd includes
6) Funky part here. Leave this window open, and open a *new* instance of Git Bash. Yes, you will have two windows open now - one for your local connection (LOCAL:), one for your remote connection (REMOTE:).
7) LOCAL: You should be in your home directory, "cd ~" if not
8) LOCAL: cd Projects/includes
9) LOCAL: "ls" and make sure you're in the right spot - you should see your "db_connect.php" file in this directory (and nothing else!)
10) LOCAL: Copy the file using "scp", which syntax is "scp <file> <username>@<website>:<location>/<file>":
11) LOCAL: scp db_connect.php <USERNAME>@<yoursite.com>:~/includes/db_connect.php
12) REMOTE: "ls" and you should now see your file!


Yesterday, we made the "User" table, and went ahead and made a test record: Bob Bobkins.  So, let's see if we can "Read" first, since it's the primary thing that will be used more often than not.

In our web root directory, have the index.php:

Code Sample:
  1. <?php
  2. require "db_connect.php"; //load config from /includes if pathed in php.ini
  3.   //OR
  4. //require "../../includes/db_connect.php"; //<yoursite.com>/websitetest.com/index.php
  5.   //OR
  6. //require __DIR__ . "/includes/db_connect.php";
  7. require "controller/users.php"; //pass to controller
  8. ?>

* Pick an option for calling db_connect.php.  Go with whatever one works, it honestly doesn't matter, and varies greatly by setup.  This may take trial and error ("~/includes/db_connect.php" worked for me).  After each change, "git push" to your site, and see if the error message pops up ( replace the controller require with an "echo 'success!' to check script progress).  If no error, you're good!

Onto our reading test.  For purposes of just trying to read, we'll test straight from the controller.  Once we can read and understand how that works, we'll move the database information into the model.

Code Sample:
  1. <?php
  2. $sql = "SELECT * FROM users";
  3. $result = mysqli_query($mysql_db, $sql);
  4. if(!$result){
  5.   die('SQL Error: ' . mysqli_error($result));
  6. }
  7. while($row = mysqli_fetch_assoc($result)){
  8.   echo $row['name']; //list all names
  9. }
  10. mysqli_free_result($result);
  11. ?>

* All this will do is display "Bob Bobkins", when successful.
* $sql contains the...sql...statement, pure and unaltered:
  - SELECT = read from table
  - * = all fields, we could name individual fields if we wanted.
  - FROM = designates what table(s) to read "from"
  - users = the name of our table.
  - Simple syntax form: SELECT <fields> FROM <TABLE>
* mysqli_query(connection, sql): is what sends the command to SQL to process, and returns a link to the result.  It doesn't necessarily contain what we need directly.
* mysqli_fetch_assoc(result-link):  THIS is how we get the information from the database.  There's a few ways we can get data:
  -mysql_fetch_array() = get results as an array, defaults to both "numeric" and "associative"
   - Numeric array: elements are represented as numbers.  "0" refers to the first element, which depends on the SQL query and/or the order of the fields in our table.  In this case, likely to refer to "id" field in our table, since it is "first".
   - Associative array: elements are represented by labels.  "name" refers to the "name" field.
   - mysql_fetch_array(result, MYSQLI_ASSOC) is EXACTLY the same as mysql_fetch_assoc(result).
  - This function is put into a loop.  Every time this function is called, it retrieves one "row" from the database.  If we had more test records in our database, this loop would retrieve all rows and display the names.
* $row[field] = the associative array, referring to the field. Try changing this to other field names and get them to display.  For a "fun" way to display all fields and their contents, replace the line with this monster:
foreach($row as $key=>$value){
echo $key, "=>", $value, "<br>;

  - this will display each field and their contents.
  - note that the "password" is showing 1234.  Yes, it's that easy to display a plaintext password.  We'll deal with this a tad later.
* mysqli_free_result(result-link) = ok, technically we don't need this.  It's a good practice to get into, however (so is mysqli_close(database-link)), especially for large scripts and multiple calls.  All it does, is free up memory that is allocated to the result.  Imagine cases with multiple databases with multiple table calls using multipule variables - ugly, right?  It's a common occurrence, however.  It's important to remember memory management - while PHP is a language that handles garbage collection automatically, there's some other languages (C++) that require manual calls, or things can get "rather messy".

Ok, we have "Read" covered.  Tomorrow we'll cram our database stuff into the model, and then start working on an input form, which will reside in our view.

php, tutorial, security, database, include, path,


Web Development From Scratch, Day 5

So far, these tutorials have all been about setting up the *environment* that we'll be working in, and not necessarily the actual coding part of things.  Typically each user will develop their own working environment, but it helps to have some basic tools setup to actually do work.  It's like signing up for a painting class, and on the first day you're shown how to paint a still life, but you're not shown (or given) any brushes, paint, or even a canvas!  From my perspective, this is exactly what happens to people trying to get into web development.

There's plenty of resources out there on how to do certain things in Web Dev, which (hopefully) after this set of tutorials, you'll be able to use your tools and self-teach yourself new things.  So yeah, all that ranting out of the way, because...on to actual coding!  Sort of!  Let's review our environment/tools so far:

1) Folder organization.  Horribly easy to overlook, but organized folders (especially setup as MVC) makes finding files simplified.
2) Text Editor.  I suggested Sublime Text 2.  Setup the file manager to view all folders in the project directory.
3) File Repository.  GitHub is where it's at.
4) A website.  I didn't do a tutorial on this one - there's too many hosts out there to do an actual tutorial on.  HOWEVER, this step is "easy", and actually, the hosting company can more than likely live-chat you through the steps to get your site going.
5) Terminal Client.  We're using Git Bash for this, but this isn't the only option.  A popular choice for SSH (among other things) is to use PuTTY
6) FTP Client.  Yes, we're trying to avoiding using this on purpose.  FTP is so 1990's.  However, it's a good idea to have one installed for files not included in our repositories.  FileZilla works great for this.
5) MySQL.  Using the server path, username, and password, able to work with databases.  Today's tutorial will cover this, however, SQLBuddy or phpMyAdmin is the tool of choice for this part.

Important note:  Almost all of these are free.  You shouldn't have to pay for these tools, except for SublimeText2.  It's worth the cash, but to remain in the spirit of "free", an alternative is Programmer's Notepad (been a while since I used this one!).  Using plain old Notepad is just...it's doable, but there's better tools for the job.  Even a crap one with nothing but a file manager is going to save a ton of headache.

Man, it's like I'm purposely avoiding getting to the coding part...enough!


Yay! We finally get to work with some PHP.  YES.


First, MySQL!  Hopefully you've setup and created the database "test" on your website.  If not, we're going to have problems.  Well, *you're* going to have problems.

The majority of MySQL tutorials go about telling you how to throw something together.  This is a tutorial on getting started in Web Development, however, so the more technical aspects will be left up to you.  The good thing is that for the most part, setups and working with MySQL is rather simple (varies widely on the job!)

We have our "test" database and are ready to immediately jump in and start creating tables and fields all over the place, right!?  WRONG.

1) Write out a database schema.  Yes, WRITE. IT. OUT.  Sure, it's fun to just whip up fields and tables as you need them, but in practice, that's a great way to cause a big mess, especially with larger databases and projects.

So the first step for our schema is to take a general overview of the entire project, and then map out what exactly we need to store into a database.  Our example project is going to be a website membership system.  Thinking about this all at once is a bit intimidating, but work on it one piece at a time, and you'll suddenly have a great system going.

Let's write this out!

Title:  Website Membership System
Purpose: Users can create and manage their profiles and information through a website.
- Login
- Register
- Profile View
- Profile Edit
- Logout
Database: "test"
  id     int(8)
  username   varchar(35)
  password   varchar(50)
  email_address  varchar(50)
  name    varchar(255)
  created   int(11)*
  modified   int(11)*

The reasoning behind each field:
  "id" = the row id. This is a PRIMARY UNIQUE NON-NULL AUTOINCREMENT INDEX.  Scary, huh?  More defining:
   PRIMARY = only one primary field per table, the "boss" of the whole thing.
   UNIQUE = no duplicate values are allowed within this field
   NON-NULL = when a field is empty, it's NULL.  Not even "" is stored, it literally means there is nothing there.
   AUTOINCREMENT = every time a new row is added, increase the counter by one.  MySQL has a counter per field in these cases, so that if you create record #532, delete #532, and then create a new record, it will be id #533 (and you'll have a gap in id's between 531 & 533).
   INDEX = in short, this means to "make it faster".  Do not apply INDEX to every damn field in the table.  Only index high-use fields.
  "username" = the user id that is entered when logging in
  "password" = the password matching the user id.  Note that we'll handle encryption on the PHP side.  NEVER EVER store passwords in plain-text!  Seriously!  NEVER!
  "email_address" = I hope this one is obvious.  By the way, I should note that I like to name fields with underscore separating words.  An alternative is to camel-case, so that it would be "emailAddress".  Whatever you use, BE CONSISTENT.  We use this field to email the user in case of lost-passwords.  If you use this for spam, I will punch you in the face.
  "created" = the timestamp of when the record was created.  It's sometimes nice to know when things are created.
  "modified" = similar to created, but stores timestamp whenever the record is changed.  This is great for monitoring activity.

Let's define a few of these datatypes:
"int" = integer.  It's a number.  The number in parenthesis (8) means that a number up to 8 digits can be stored.
"varchar" = variable character.  a-z, 0-9, symbols, all that fun stuff.

Special note regarding the "int(11)" for "created" and "modified":  We could use DATETIME and TIMESTAMP respectively, but the reason why we're not going to use that here is to avoid converting from PHP to/from MySQL. PHP uses timestamps for everything related to date and time. It's an integer (number) that counts the number of seconds since midnight on 1970Jan01GMT.  That's when the world was created.
  - MySQL has 3 date types: DATETIME, DATE, TIMESTAMP.
   DATE = stores as YYYY-MM-DD
   TIMESTAMP = DATETIME, but automatically updates to the current time every time the record is altered
  - The issue would be that for every database call, we'd have to convert (handled through our PHP or in the SQL query itself).  PAIN IN THE ASS.
  - Although, it comes down to what you need to use these for.  When storing as an "int", you lose all of MySQL's built-in time and date handling procedures.  In this case, we're handling everything on PHP's side, so that's the reason for the int.


Quite a few words for so little so far, right?  The major thing to know about databases is that it's more strategy than storage.  Databases need to be easy to navigate.  It is really easy to complicate a database to the point where not even the creator knows what is going on (common with "let's slap this on" developing).


Ok, here's another tool for your arsenal.  The majority of web hosts supply phpMyAdmin with websites.  You can see if it's already setup for you by appending http://<yoursite.com>/phpMyAdmin - if a login screen appears, it's setup for you!  If not, don't fret.  Go into your website setup...under the database section, your "test" database should be listed.  Depending on the host, they should have a "edit" or "manage" or "something" link by that database.  Click it, and you'll likely be sent to a weird address like http://asgbqiurwegbsqlmanager.server.something/words/numbers/seconds/login.php - PUT THE LINK IT GIVES YOU INTO YOUR BOOKMARKS.



Well, that way you can access the login page directly without having to launch your webhost control panel every time.  Also, this is more than likely the address you'll use when setting up your own database manager (like phpMyAdmin).

Regardless, in your website setup, you should have the following information:
- Server
- Username (it better be different than your website account login name!)
- Password (again, different, and complicated!)

Now, before we proceed, let's think about at least putting a manager on the website ourselves.  Snag SQLBuddy or phpMyAdmin.  Toss either one of these into your webroot subdirectory, such that it is accessible as http://<yoursite.com>/sqlbuddy

Fire that up in your browser and try to login with the details you setup and recorded from your webhost.

Aaaaaand now you should be presented with a fairly-empty looking view of your "test" database!

Looking around, it's not very intuitive what you should do with everything that's presented.  This is the part where you pull out your database schema to work from.

1) Create Table - name "users"
2) Number of fields "7" (or you may add them one at a time, depends on the client you're using)
3) Fill out the fields to match the schema.  Set the Collation as UTF-8.  Hmm, perhaps we should have covered this.  Basically, put everything as UTF-8 for now; again, depends on the job.
4) For "id", make sure you mark it as "PRIMARY", with option "auto_increment".

When complete, and hopefully no errors, you'll be presented with the structure of the table you just created, listing all the tables.  MAGIC.  

1) Depending on the client, click "INSERT".
2) Fill out everything with bogus values. This will be our "test" record:
id: <blank>
username: test
password: 1234
email_address: email@address.com
name: Bob Bobkins
created: <blank>
modified: <blank>

Submit it, and we're done!  SO EASY.  Get used to working with this environment, since it's a no-holds-barred way to work with your database data.

php, tutorial, mysql,


Web Development From Scratch, Day 4

Alright, looks like my SSH request finally went through.  Let's go ahead and set this up for today's lesson (yes I know you have your database "test" created - we'll get to that soon enough!)

First off, from your terminal...oh right, we're in Windows.  Open up Git Bash.  Now, from *this* terminal, type:
ssh <username>@<website>

So, for example, your username to ssh into your site is "Blah", and your site is "www.websitetest.com", your line will appear as so:
ssh Blah@www.websitetest.com

niiiiiice.  Type in your password, and you'll be at a funky looking prompt, which varies *greatly* among hosting servers:

Yeah, weird, right?  Type "ls".  Chances are, there isn't much here.  Probably your "www" folder (or some variant).  "cd" into that, and you'll see all your web files.  This is your "web root" directory, that is, it's the directory that appears when you type in "http://www.websitetest.com" into your browser.  Note that it'll usually load "index.html" first.  Ok, done poking around?  "cd .." or "cd ~" to get to the home directory.

Now, the part I really can't help with here is making sure your host has Git installed and enabled.  Again, since there's many hosting companies, and every one is different, they may or may not have it installed.  A quick test is to type "git" at the command prompt.  If it's installed, it will show you a list of commands, with something like "See 'git help <command>'..." at the bottom.  If so, YAY.  If not, welp:

1) Google your webhost and "git".  See if there's any easy way to get git on there.
2) If not, unfortunately you'll probably have to stick with ftp for now.  (Get a better webhost! I suggest A2 Hosting - they have Git installed for sure!)

Keys.  Sigh.  Now, unless you feel like typing in your password every time Git or you connects via SSH to your server, you're going to have to make authentication keys.

1) If you're still on git bash, and connected to your webserver, type "exit" to disconnect.  
2) Now, just to make sure, type "cd ~" to put yourself back at the home directory.
3) View all files with "ls -al"
4) look for a ".ssh" directory.  If one exists, you'll want to back it up (or if you know you're already using it - say, you already set it up for git, you can use the same keys! - skip to <STEP> if you want to use the same key)
5) You can refer to GitHub SSH Key Guide if you want to generate keys, or to make new ones (don't forget to add them to Git!).
6) Arguable, but leave the passkey blank so that you don't have to type it every time you git/login.
7) Open up "id_rsa.pub" by any means necessary.  Well, you can do this directly through Git Bash.  Type "cat id_rsa.pub".  It'll display the contents of the file.
8) Now, to copy *from* Git Bash, Click the top-left window icon and Edit -> Mark, then drag a selection box over the text (make sure you cover *all* of the file contents!).  Then press Enter.
9) Ok, ssh back into your webserver.
10) At your home directory, "cd .ssh".  If the directory doesn't exist, create it: mkdir .ssh
11) In the directory, type: vi authorized_keys2
12) A really strange-looking setup appears.
13) Press i  (when you do, the bottom of the window should say "-- INSERT--")
14) Paste into the window - with Git Bash, press the "Insert" key to do this.  This should paste the entire "ssh-rsa <bunch of characters> email@address.com" into the top line.
15) Press ESC (removes the "-- INSERT --" at the bottom of the screen)
16) Type :x
17) Press enter (":x" <enter>)
18) You're magically at the prompt again! whew that was scary!
19) confirm the file actually contains what you pasted by typing "cat authorized_keys2"
20) exit
21) Relog back into your ssh server - this time, it should log in without a password (however, it will ask for a passkey if you set one!)

And that's "it".  Again, you only have to set this up once (unless you access from a computer with different keys!)


Ok, now, we have to somehow magically make Git push to your webserver.  This is another one of those "setup once" things.

1) ssh to your server.
2) First, we're going to make a subdirectory on the web root, so that it will be accessible from http://<yourwebsite.com>/websitetest.com.git
3) cd into your webroot, which varies by server.
4) mkdir websitetest.com.git
5) pwd
6) With "pwd", it shows the full path to the current directory.  Remember this!  Again, it varies by server, and will look something like:
7) cd ~
8) Type: mkdir websitetest.com (create the folder within your home directory, different than the ".git" one in webroot)
9) cd websitetest.com
10) git init --bare
11) The home/websitetest.com directory is now initialized with a "bare" repository.  This is separate from your repository on github.com, but we'll be pushing directly to this one.
12) Now, this next part is called the "hook".  Remember the path you got from pwd?  I hope you wrote it down!
13) vi hooks/post-receive
14) Press "i" for insert mode.
15) Enter in the following, replacing <PATH> with the full path you got from "pwd"
GIT_WORK_TREE=<PATH> git checkout -f

16) Press ESC, then :x
17) Verify the file with "cat hooks/post-receive".  If it has what you entered, you're good so far.
18) chmod +x hooks/post-receive
19) The file is now marked as "execute".
20) exit

We're back on the local machine for this next part.  I promise we're almost done.  Seriously.

1) Open Git Bash, if for some reason you closed it within the last 2 seconds of the previous section.  "cd ~/Projects/www.websitetest.com"
2) Now, to setup our remote access and push to our webserver!
3) git remote add web ssh://<username>@<yourserver.com>/websitetest.com
    Note: Make sure it's pointing to the repository, and not the .git directory you made in your web root.
        - if you get "repository not found", you may have to set it up as:  ssh://<username>@<yourserver.com>/~/websitetest.com
4) git push web +master:refs/heads/master
    Note: this pushes the master config and goodies and all that - you only have to type "git push web" for normal use

Note that if you get an error...tough!  Well actually, google the error.  It helps to google your host, git, and error message, such as "godaddy git git-receive-pack:command not found".  I apologize if you're using GoDaddy.  They don't have git installed, and are kind of a pain to get working.  However, I'm nice, and managed to run across this post when I tried that search: GoDaddy git-receive-pack error fix

5) Everything should be happy, but, there's only one way to be sure!
View your work at:  http://<yoursite.come>/websitetest.com.git/
For me, that's: http://www.sykohpath.com/websitetest.com.git/

Hey look at that...I'm Awesome.  Super Awesome.

Now, just to make sure, we have 2 "remotes" setup.  Simply type "git remote" to view all of them.  There should now be one for "origin" and one for "web".  Origin points to your repository on github, while web points to your website.  With this process, you should be able to setup a "dev" and "live" environment.  I'll explain that setup in a separate tutorial.  For now, be happy you can push to git and your website!

And finally from now on - after you commit your changes, all you have to type is "git push origin" or "git push web".

php, tutorial, git, github,


Web Development From Scratch, Day 3

Hey thanks for losing my really long post I did this morning!  siiigh.

After writing out a freakin' novel on setting up your environment for a basic MVC setup, I'll have to cut corners as I try to recreate this post.

1) Create the following file structure in your "www.websitetest.com" directory:
|- /assets
|- /controller
|- /model
|- /view

WHY?  bah just do it. seriously.

MVC is typically reserved for "more advanced" areas, but why not do things right the first way?  Newer programmers will create scripts that contain all their code in one file, and then have that file stuck in the root directory.  Yes, the code "works", but that is not the "correct" way to go about things.

Quick MVC breakdown:  Model, View, Controller.  Breaking it down into what these three actually are:

Model: Database procedures = php + mysql
View: Frontend = html (assets will contain Javascript, CSS, and any other front-end properties)
Controller: Logic = php

With that understanding, let's see it in action.  First off, let's start off with a single-file "spaghetti-code" file.  I mean honestly, this doesn't look to scary, but imagine larger projects where everything is in one file.  Say something breaks in the logic?  Instead of scanning one large file, with MVC, you know to check your controller, which will be a much smaller file with cleaner code.

Couple notes:
* Use "mysqli" functions instead of "mysql" functions.  MySQL has been deprecated due to security issues.
* I'm using procedural style for these examples, since OOP tends to scare off newer programmers (we'll cover this later).
* While I'm using requires here, later on these are unnecessary with better techniques.  Keep this in mind: there's almost always a more efficient way of doing things!  Since it requires some OOP understanding however, let's keep it "simple" for now.


require "db_conf.php"; //load database configuration files

$mysqlDB = mysqli_connect($mysqlServerName, $mysqlUserName, $mysqlPassWord);
    die("Unable to connect: " . mysqli_connect_error());    

function get_all_users($db_conn){
    $sql = "SELECT * FROM users";
    $result = mysqli_query($db_conn, $sql);

    $allUsers = array();

    while($row = $mysqli_fetch_assoc($result)){
        $allUsers[] = $row['firstName'];

    return $allUsers;

$userList = get_all_users($mysqlDB);

            foreach($userList as $value){
                echo $value, "<br>";

Now, for MVC, we'll be splitting this up into multiple files.  Our original index.php becomes nothing more than to load the site configuration (in this case, database settings), and then load up the requested controller.  Later on, you'll be able to choose what controller to load by simply appending options to the URL (h--p://site.com/index.php/users).

    require "db_conf.php"; //load config

    require "controller/users.php"; //pass to controller

Yes, only two lines of code now!  Let's have a look at the controller, which will be the first file called:

    require "./model/user_database.php"; //load model

    $mysqlDB = connect_database($mysqlServerName, $mysqlUserName, $mysqlPassWord);

    $userList = get_all_users($mysqlDB);

    require "./view/all_users.php"; //pass to view

Not scary at all, is it!  This is the "Logic" file, which is the main file for handling the control of the flow.  It loads the database model, calls function to connect to the database, gets all the users, and then passes control to the view.

Now, let's take care of the database functions, which are all under the Model category:

    function connect_database($server, $name, $pass){
        $link = mysqli_connect($server, $name, $pass);
            die("Unable to connect: " . mysqli_connect_error());    
        return $link;

    function get_all_users($db_conn){
        $sql = "SELECT * FROM users";
        $result = mysqli_query($db_conn, $sql);

        $allUsers = array();

        while($row = $mysqli_fetch_assoc($result)){
            $allUsers[] = $row['firstName'];

        return $allUsers;

Notice that the "get_all_users" function is exactly the same.  That's because it was a database function to begin-with, so no change needed.  We put the database connection routine in this file as well, since it's well, a database connection.

Finally, we have the "View":

            foreach($userList as $value){
                echo $value, "<br>";

Again, this part is unchanged.  Previously, it was "tacked on" to the end of the file.  Also note that there was a whitespace before the html in the previous version...oops!

One final note...This is completely optional, and there's arguments either way.  I won't get into gory details, but basically:  You don't, and also shouldn't end your php files with "?>".  WHY?  Several reasons:

1) There can be whitespace after the bracket.  This is the main issue.  ANY whitespace before html output can and will cause problems.  I mean, it's not difficult to make sure there isn't any trailing whitespace.
2) Zend Framework coding standards.  Get used to seeing "Zend" quite a bit in the future.  They require any file that is pure-php to not have the closing tag.
3) This is by design: PHP documentation even states that this tag is optional - useful to leave out for includes(), requires(), and output buffering.

Of course, there's reasons why you should always close the tag:
1) OCD.  Tags were made to be in sets. Open-close.  This applies to just about every other tag on the planet.
2) Can be confusing to others to not having the closed tag in there.
3) There is no 3

You may sense my bias here.  You're not a "bad programmer" if you're using/not using tags.


Ok, so, on your web host service, let's get this setup for tomorrow.  Every host is different, but basically what you need to do is create a MySQL "test" database.  Don't need to bother with anything else in it right now, just get that created.

php, tutorial, mvc,


Web Development From Scratch, Day 2

Ok, so, we got through day one.  We used Git GUI to handle out keys, because for some reason the terminal part is scary.  Welp, time to not be scared anymore.  Technically you can use Git GUI for everything, but it's actually going to be faster to work from the terminal.

1) Open Git Bash.
2) Cower in fear of the terminal window.

Once you're done cowering, you'll need to know a couple basic commands (write these down!)

Change to your home directory
cd ~

Change to Parent directory
cd ..

Change to Root directory
cd /

Change to a subdirectory (replace <NAME> with the folder name, such as "Projects")
cd <NAME>

Navigation is fun, but need to actually be able to see *what* is in the directory:

You'll be using these "all the time", but at the beginning, you'll only be using these to get to your projects directory, and then sitting in that same place while you spam "git" commands.

NOTE: if your "projects" directory is on another drive, or rather, to change drives in general:
cd /<drive>

So "cd /D" would put you at the D: drive.  Again, only for Windows.

Since we didn't do this yesterday, let's go ahead and whip this part out: configure your username and email.

In the Git Bash terminal, set the following:
1) git config --global user.email "<EMAIL ADDRESS>"
2) git config --global user.name "<USERNAME>"

Replace the fields with your values:
git config --global user.name "SyKoHPaTh", for example.  


So, by default, when you open Git Bash, you're in your home directory.  If for some reason you aren't, do the "cd ~" thing, and you're magically back home.

1) Type "ls", and you will see something similar to your explorer-home window, with your user folders.
2) Type "cd Documents", followed by "cd Projects"
3) Another "ls" will show you your lonely "www.websitetest.com" folder.  Don't go into it quite yet.

Setup Git Repo Locally
1) Only have to do this once per repo (unless you screw something up and want to start over hah!)
2) type "git clone git@github.com:<USERNAME>/websitetest.com.git" - replace <USERNAME> with yours, so for me, it's: git@github.com:SyKoHPaTh/websitetest.com.git
    Note: This is also how you can clone other people's repos!
3) This created a new folder named "websitetest.com" next to your "www.websitetest.com".  This is intentional, because a) can't clone into a non-empty directory, and b) good habit to have a "backup" of sorts, by doing...
4) rename "www.websitetest.com" as "www.websitetest.com.backup" (or however you want to name it).  You can do this through explorer, or if you're finally confident with git commands, you can do it through the terminal:
mv www.websitetest.com www.websitetest.com.backup
    Tip:  instead of manually typing out the whole folder name, type in "ww" and then press TAB.  OMG magically filled in for you!  If you press tab and nothing happens, press it again, and it'll show you a list of matches - simply type more of the name you want to get it to complete.
5) Now, rename "websitetest.com" as "www.websitetest.com".  Easy, mind-numbing organization crap we're doing here, right?  Also, what about that awesome index.php file that's in the backup directory?  Just copy that over - through explorer, or again, terminal:
cp www.websitetest.com.backup/* www.websitetest.com

Now, you can skip this part, or if you feel like poking around:
1) Go into your www.websitetest.com folder using the terminal:
cd www.websitetest.com
2) Type "ls", and you'll see your two files: index.php README.md

How does git "know" how to handle this folder as a link to your repository?  New command (flag)!
ls -a
This displays all files, including hidden ones.

1) You'll see a .git folder.  "cd .git" to get into it.
2) "ls" in there, and you'll see all of git's innards.  Do NOT dink around with any of this stuff.  Just be aware  it's there.
3) Finally, "cd .." to get back to where we were.

FINALLY, we can start using git.  For today, we'll just mess around with the local-side, and not server side stuff.

New commands!  You already used "git clone" to get this far, now we need to use git to update and automagically upload our file changes to our repository.

1) git status
2) This shows all files that have changed, and are ready to be dealt with.  Actually reading what it says, we need to "add" our awesome index.php file to the repository.
3) git add index.php
    Note: Alternative: "git add *" will add *everything*.  Be careful doing this around sensitive files!
4) "git status" again, and it now says "Changes to be committed".
5) This next command is the one you'll be using all the fricking time: git commit.  Specifically, you'll be using it as follows:
git commit -a -m "<COMMIT MESSAGE>"
    -a means "all files that are ready to be commited"
    -m means "attach this message to the committed files"
    <COMMIT MESSAGE> is, well, whatever you want!
6) git commit -a -m "First commit, my awesome file!"
7) git status
8) Notice that no files are listed in status now.  This means they're ready to be "pushed" to our server.  You'll see the soon-to-be-familiar "Your branch is ahead of 'origin/master' by 1 commit."
   Note: "origin" is our reference to "<USERNAME>/websitetest.com" repository, while "master" is the "branch" of the repository.  More on these shortly.
9) And finally, the next command you'll be using for the rest of your life: git push.  Technically you want to use it as "git push origin master", but for now, "git push" will work.

Go back to [url=http://www.github.com]GitHub, and under your profile name, go to your "websitetest.com" repository.

1) You'll now see your "index.php" file stored in the repository, with the commit message attached to the side of it.
2) Click on the "index.php" file itself, and it will show you the contents of the file.  In this case, it will say "I'm Awesome", if you filled it out from yesterday.
3) Click "Edit", and OMG, you can edit this file directly through the site!  Type in "Super Awesome!", and put a commit message in the box under the edit area: "Added awesomeness"
4) Click "Commit Changes", and it will take you back to the file contents view.  Click "History".
5) Here, you can see all of the commits attached to that specific file.  This is HIGHLY useful, especially if you "accidentally" delete a large chunk of code and commit it; simply view the previous commits, and your code is still there! (Click "Browse Code" by "First Commit...", and you'll see the file contents before you added "Super Awesome!").
6) Click the giant blue "websitetest.com" to get back to the file view.
7) You'll see your newer commit has replaced the first commit, everything is good so far.


Ok, back in your terminal, type:
1) git pull
2) Notice that it lists your file "index.php" with a number, and + next to it, reflecting 1 line was added (- is 1 line removed!)
3) Open up index.php by any means necessary (hopefully you have SublimeText 2 running, which will detect changes in the file if you already have it open).  Yeah, make sure you have Sublime Text 2 as the default editor for .php files.  Heck, make it the default editor for everything.
4) You should now see Super Awesome added in your file!

See, at this point, consider that now all you have to do, is "git push" from your local computer, and then a "git pull" on your server...with those two commands, you'll update all the files with changes on your server, no messing around with ftp or anything like that!

It can even be made *easier* so that you can type something like "git push production frontend", and it will handle copying everything from your "frontend" branch to your "production" server.  That's beyond the scope of this document right now, but be aware that this functionality exists.


git status
git commit -a -m "message"
git push
git pull

Good enough for today?  Sure!

Actually WAIT.  This next part may take a day or two, depending on your webserver.  You have a website, right?  And it's with a crappy hosting company, right?  Heck, the disclaimer on mine says it can take up to 72 hours for it to be "enabled".  WELL THEN.

This varies greatly by hosting company, but the gist of it is:
1) Go to your hosting company website.
2) Account Settings for your website
3) Click SSH
4) Enable

Chances are you'll have to verify, which is a good thing.  Do it.  Do it now, because tomorrow's lesson will be working with the remote server stuff.

Hey look at that, I forgot to enable SSH on my test hosting server.  That's not awesome.  Welp, doing it now.  If it's not up tomorrow, I'll have to think of a different tutorial.

php, tutorial, terminal, git, github,


Web Development From Scratch, Day 1

SO, you want to get started in web development, eh?  Sure, you can crap out an html page and slap on a "I DID THIS" on the bottom, but that won't even start to get you a job, anywhere, unless you want to get ripped off by people on craigslist ("will pay in stock!").

Long gone are the days of working in notepad, ftping each file, and then f5'ing your way to success.  We're in the future, now, so let's do things appropriate to modern times.

Objective: Really basic starter setup.  I'll be leaving out a ton of the "more advanced" (from a beginner perspective) elements, and just get things up and running as quickly as possible.  Note the date of this post.  This isn't necessarily a living document, so yeah, things will change.  Enough babble, let's go!

Hardest part is the setup.  Good thing is, you only have to do it once (more or less lol).  Follow this EXACTLY omg.

1) Chances are, you're working in Windows. Personally I work in Linux, but Windows is in the majority right now.
2) File Structure.  Do This.  Seriously:
3) Start -> Documents -> Create Folder: "Projects"
4) Go into that folder, and Create another one: "www.websitetest.com"

Sublime Text 2
1) Download Sublime Text 2
2) Install it, and run it.
3) Project->Add Folder to Project
4) Navigate to "Library -> Documents -> My Documents -> Projects".  Add that one, not the subfolder you just created.
5) Now, whenever you create a new folder in your Project folder, it will magically appear in your file browser in SublimeText2.
6) On the left side, you should see your "www.websitetest.com" folder.  Right-click on it, and select "New File".
7) Type in "I'm Awesome", then save it as "index.php"

1) Go to GitHub and create an account.
2) Go through Step 1.  This will install Git on your computer.
2a) Add your SSH Key: Account Settings -> SSH Keys
3) As tempting as it is to use Git Gui, I prefer Git Bash.  Since you're a noob and are afraid of words, we'll go through Git Gui for now.
4) Open Git Gui!
4a) Click on Help, "Show SSH Key"
4b) It's blank.  Or it shows something.  If it shows something, welp, back it up.  
4c) Click "Generate Key".
4d) Press "Enter" for a blank passphrase.  Again.
4e) "Copy To Clipboard"
4f) Back on GitHub.com, click "Add SSH Key"
4g) Title: Name it something like "<computer name> Windows 7 (blank)"
4h) Paste the key, then confirm.
5) Click back to your account, and click "Create Repository"
5a) Name: "websitetest.com"
5b) Set as Public, and check the box "Initialize README"
5c) When that's done, you'll see your little repo box.  Click it!
5d) Notice the contents "https://github.com/<username>/websitetest.com.git".  Remember this, since you'll be accessing it tomorrow in the form of: git@github.com/<username>/websitetest.com.git

aaand that's good enough for one day.  Yep, leaving you hanging here!  Tough.

php, tutorial, sublime text 2, git, github,


Maze Generation - Recursive Division

I've been in the mood to make a sort-of Gauntlet clone, so this weekend I decided to mess around with maze generation a little bit.  On Wikipedia, there's several algorithms to generate a maze, but only steps listed for the recursive one.  I wanted to make a maze setup with random-size corridors, so this is what I came up with.  This is in C++, and yes, I know it's not preferred to pass a 2d array into a function - use a vector instead.

xS/xE = x start / x end
yS/yE = y start / y end

The 2d world array:  int world[worldWidth][worldHeight]
REALWORLDHEIGHT is a const global.  Again, yeah, I know, need to use vectors instead of arrays for this part.

Code Sample:
  1.     void maze_recursive_division(int (*world)[REALWORLDHEIGHT], int xS, int yS, int xE, int yE){
  2.         if(xE-xS < 10 || yE-yS < 10){ return; } //too small to divide chamber (adjust to make larger/smaller chambers
  3.         //1) divide original chamber into two walls, at least 1 space wide (using 2 for this)
  4.         int xDivide = (rand() % (xE-xS-2)) + xS+2;
  5.         int yDivide = (rand() % (yE-yS-2)) + yS+2;
  6.         for(int x = xS; x < xE; x++){
  7.             if(world[x][yDivide] == 60){ //60 is "walkable" tile, 10 is "wall"
  8.                 world[x][yDivide] = 10;
  9.             }
  10.         }
  11.         for(int y = yS; y < yE; y++){
  12.             if(world[xDivide][y] == 60){
  13.                 world[xDivide][y] = 10;
  14.             }
  15.         }
  16.         //2) put "holes" in 3/4 of the new walls
  17.         //xs->xd
  18.         //xd->xe
  19.         //ys->yd
  20.         //yd->ye
  21.         int solidWall = rand() % 4; //0 1 2 3 - pick the wall to leave solid
  22.         /* gap
  23.         length:  pick between start and end:  xS -> xE   rand xDivide-xS
  24.         Start point:  Pick between start and End-Length:  xE-Len-xS + xS
  25.         */
  26.         if(solidWall != 0){ //xs -> xdivide
  27.             int gapLength = (rand() % (xDivide-xS));
  28.             int startPoint = rand() % (xDivide-gapLength-xS) + xS;
  29.             for(int x = startPoint; x < startPoint + gapLength; x++){
  30.                 world[x][yDivide] = 60;
  31.             }
  32.         }
  33.         if(solidWall != 1){ //xdivide -> xE
  34.             int gapLength = (rand() % (xE-xDivide));
  35.             int startPoint = rand() % (xE-xDivide-gapLength) + xDivide;
  36.             for(int x = startPoint; x < startPoint + gapLength; x++){
  37.                 world[x][yDivide] = 60;
  38.             }
  39.         }
  40.         if(solidWall != 0){ //ys -> ydivide
  41.             int gapLength = (rand() % (yDivide-yS));
  42.             int startPoint = rand() % (yDivide-gapLength-yS) + yS;
  43.             for(int y = startPoint; y < startPoint + gapLength; y++){
  44.                 world[xDivide][y] = 60;
  45.             }
  46.         }
  47.         if(solidWall != 1){ //ydivide -> yE
  48.             int gapLength = (rand() % (yE-yDivide));
  49.             int startPoint = rand() % (yE-yDivide-gapLength) + yDivide;
  50.             for(int y = startPoint; y < startPoint + gapLength; y++){
  51.                 world[xDivide][y] = 60;
  52.             }
  53.         }
  54.         //3) call subdivide on smaller chambers
  55.         maze_recursive_division(world, xS, yS, xDivide, yDivide);
  56.         maze_recursive_division(world, xS, yDivide, xDivide, yE);
  57.         maze_recursive_division(world, xDivide, yS, xE, yDivide);
  58.         maze_recursive_division(world, xDivide, yDivide, xE, yE);
  59.     }

maze generation, c++,


Lone Star PHP Conference

Lone Star PHP

June 29 & 30th 2012
Addison Conference Center
Addison, TX

php, conference,


Mallyx Solo Hero Build

Player: Ancestor's Rage, DwG, Spirit Rift, Gaze of Fury, Light of Deldrimor, ebon Wisdom, Lightbringer Signet, Death pact signet | Expertise 12 Channel 12
Dervish: OgmlYeppqtasLheNflJig1tVjRf9eB - Disable all enchants at mallyx
Ritualist: OAKjAyiIpOYTrnxOcyMhmQHnJ - soul twist
Necro: OAhjYghr4SXTlTMTVVTOSTDTciA - healer
Necro: OAhjYghsoOW7Mm0cyNMrqMzxJ - weapon healer
Necro: OAhjYggcoOO7v5mLaP5kmDzqK - ghost might spam
Mesmer: OQhTAWBPshGkDmemulfGg0lcB - panic
Mesmer: OQhTAYhOwJEmDIdTvlfGw0lcB - instable

- Note: take along a rez skill, and a conset for a definite win (after practice, of course!)

- Waves: Prioritize Rage Titans and Tortureweb Driders
  - It's best to clear all the "little crap" before the "big crap", to prevent the next wave from spawning

- Disable Enchants and Hexes for Mallyx, micro Gaze of Fury to kill his spirits

guild wars,


SSH Tunnel

Mainly for my reference since I can't seem to remember exactly:

ssh -D <port> <user>@<host>

Configure software to use SOCKS proxy:

localhost:<port> OR using <port>

linux, reference,


Find Match in Array of Objects

Short one today...

Normally, array_search() is awesome for finding a match within an array:

$array = array( "one", "two");
array_search("one", $array);  //true

But, in dealing with an array of objects (such as a resultset in a framework), there's not a built-in function that handles this (to my knowledge!).  So this function will handle it:

Code Sample:
  1. function search_array_object($needle, $arrayHaystack){
  2. foreach($arrayHaystack as $key=>$objectHaystack){
  3.   foreach($objectHaystack as $value){
  4.    if($needle == $value){
  5.     return $key;
  6.    }
  7.   }
  8. }
  9. }

So if you have something like this:
$arrayObj[0]->text = "one";
$arrayObj[1]->text = "two";
$arrayObj[2]->text = "three";

You'll get:
$key = search_array_object("two", $arrayObj);   // returns "1", the key where it was found



Sitemap XML Generator

Forgot I made this a while back...Ok, you know how there's "free" sitemap.xml generators?  And you know how they limit it to like, 10 links?  And you know how they suck?

Time to fix that.


1) Open "sitexmlgen.php"
2) Change the site to scan in line number 35:  $linklist[0] = "http://www.sykohpath.com/";
3) Place on your site
4) Run the script.  Example: www.yoursite.com/sitexmlgen.php
5) Wait until it finishes, will take long on large sites with many links.
6) When finished, view source
7) Copy ALL the text under "XML IS BELOW" section.
8) Paste into new document "sitemap.xml".
9) Done with generation!  If you don't know what to do with that file, learn to SEO.
10) there is no 10.

And here's the code.  Note that there *is* a 5000 link limit, imposed by sitemap.org:

Code Sample:
  1. <?php /* ---- Information ----
  2. Name: sitexmlgen.php  "sitemap.xml Generator"
  3. Last Updated:  20110512 080000
  4. Page Version: sitexmlgen.php v1.0
  5. Author: SyKoHPaTh
  6. ---- Version History ----
  7.   1.0  Initial Coding
  8. -------------------------
  9.   PURPOSE:
  10.    "crawl" a site and record the links if they are valid.
  11.    Note: this only picks up links between <a> tags.
  12. -------------------------
  13.   TODO:
  14. -------------------------
  15.   LICENSE:
  16.    Modification: OK, but must keep credit line: "SyKoHPaTh (www.sykohpath.com)", and this License.  Any modifications MUST be written in "Version History", with your name and/or handle, and what the modification was.
  17.    Free for public and commercial use.  If you paid for this, you got scammed.
  18. --------------------------
  19. */
  20. /* -------- VARIABLES -------- */
  21. $linklist = array();
  22. $linklist[0] = "http://www.sykohpath.com/";
  23. $sitemap_limit = 50000;  //enforced by sitemap.org, max number of <url> links in one sitemap XML file.
  24.         // there is also a 10MB limit to sitemap XML files, but we're not checking for that here.
  25. /* -------- FUNCTIONS -------- */
  26. function digger($scanlink) {
  27.   //does the work of scanning a page and putting links into an array
  29.   $linkcontents = @file_get_contents&#40;$scanlink&#41;;
  30.   if(!$linkcontents) {
  31.    print "Unable to open: {$linkcheck}n";
  32.    return array();
  33.   }
  34.   $linkinfo = parse_url($scanlink);
  35.   $linkcore = $linkinfo['scheme'] . "://" . $linkinfo['host'];
  36.   $linkcontents_strip = strip_tags($linkcontents, "<a>");
  37.   $linkcontents_mod = preg_replace("/<a([^>]*)href="//is", "<a$1href="{$linkcore}/", $linkcontents_strip);
  38.   $linkcontents_mod = preg_replace("/<a([^>]*)href="?/is", "<a$1href="{$scanlink}/?", $linkcontents_mod);
  39.   preg_match_all("/<a(?:[^>]*)href="([^"]*)"(?:[^>]*)>(?:[^<]*)</a>/is", $linkcontents_mod, $matches);
  40.   return $matches[1];
  41. }
  42. function checklink($linkcheck) {
  43.   //simply checks a link to see if it loads up or not
  44.   $linkcontents = @file_get_contents&#40;$linkcheck&#41;;
  45.   if(!$linkcontents) {
  46.    print "Unable to open: {$linkcheck}n";
  47.    return false;
  48.   }
  49.   return true;
  50. }
  51. /* -------- Initial header thing -------- */
  52. $xmloutput = "<?xml version="1.0" encoding="UTF-8" ?>n<urlset   created with SyKoHPaTh's SiteMap.XML generator  www.sykohpath.com  -->n";
  53. $x = 0;
  54. while(1==1){
  55.   //gen list
  56.   print "Scanning [$x of " . (count($linklist)-1) . "]: " . $linklist[$x] . "<br>n";
  57.   $linkmatch = digger($linklist[$x]);
  58.   //scan list
  59.   foreach($linkmatch as $key=>$value){
  60.    //print $key . ": " . $value . "n";
  61.    //filter bad data
  62.     //check link against $linklist[0]
  63.    if(substr($value, 0, strlen($linklist[0])) == $linklist[0]){
  64.     if(!(in_array($value, $linklist))){
  65.      //push to array
  66.      $linklist[] = $value;
  67.      $xmloutput .= "<url>nt<loc>" . $value . "</loc>n</url>n";
  68.     }
  69.    } else {
  70.     //check if it's a foreign link
  71.     if(!substr($value, 0, 4) == "http"){
  72.      //add scanned linklist to front, and see if it's a valid link
  73.      //cut out everything after the slash:  http://w3dev.millerind.com/parts/index.php?bid=2
  74.      $pattern = preg_replace("/[^/]*$/s", "", $linklist[$x]);
  75.      $value = trim($value); //strip whitespace BAD CODER, BAD!
  76.      $value = preg_replace("/^[/]/s", "", $value); //strip beginning / if there is one
  77.      if(checklink($pattern . $value)){
  78.       $value = $pattern . $value;
  79.       if(substr($value, 0, strlen($linklist[0])) == $linklist[0]){
  80.        if(!(in_array($value, $linklist))){
  81.         //push to array
  82.         $linklist[] = $value;
  83.         $xmloutput .= "t<url>ntt<loc>" . $value . "</loc>n</url>n";
  84.        }
  85.       }    
  86.      }
  87.     }
  88.    }
  89.   }
  90.   //echo "Total links: " . count($linklist) . "<br>n";
  91.   //if nothing new was added, exit loop
  92.   if($x + 1 >= count($linklist)){ break; }
  93.   //if limit reached, exit loop
  94.   if($x > $sitemap_limit - 1){ break; }
  95.   $x=$x+1;
  96. }
  97. //Optional tags for each link.
  98. //<lastmod>" . date("Y-m-d") . "</lastmod>n
  99. //<changefreq>yearly</changefreq>n
  100. //<priority>0.5</priority>n
  101. $xmloutput .= "</urlset>";
  102. print "<br>n<br>n-----------------------------------------------------------<br>n           XML IS BELOW (view source)<br>n           Copy and paste into "sitemap.xml"<br>n-----------------------------------------------------------<br>
  103. n" . $xmloutput;
  104. ?>

php, xml, sitemap,


試験 to do list 試験

Code Sample:
  1. user account avatars
  2. user account score total on profile page
  3. guild wars template code generator
  4. - dropdown boxes for selectable options
  5. database UTF-8
  6. - Japanese got messed up - restore those posts from old DB
  7. admin:
  8. display total # of unapproved comments
  9. display new users within past...month to start (approval)

delete, 試験,


Guild Wars 1 Template Formula

So, using some resources scattered about the 'net, I was finally able to piece together how to convert a Guild Wars template code into their respective skills and attributes.  The biggest issue I came across was on error checking, as well as not being able to find clear instructions on parts.

So essentially, this: OwpiMypMBg1cxcBAMBdmtIKAA magically turns into this: Code Sample:
  1. Template ID: OwpiMypMBg1cxcBAMBdmtIKAA
  2. Template Type: default
  3. Template Version: default
  4. Primary Profession: Assassin
  5. Secondary Profession: Dervish
  6. Attribute 1: Critical Strikes: 12
  7. Attribute 2: Scythe Mastery: 12
  8. Skill 1: Wounding Strike
  9. Skill 2: Eremite's Attack
  10. Skill 3: Mystic Sweep
  11. Skill 4: (blank)
  12. Skill 5: Dash
  13. Skill 6: Assassin's Remedy
  14. Skill 7: Way of the Master
  15. Skill 8: Resurrection Signet

You can try it here!

Now, onto how to reproduce this...

1) Convert the code from base64 into binary.  I used PHP, and unfortunately, binary conversions of "large numbers", let alone 64-bit just simply does not work.  It has to be broken down into segments, and each segment converted:
Code Sample:
  1. function base64bin($str){
  2.   //converts base64 to binary
  3.   $result = '';
  4.   $str = base64_decode($str);
  5.   $len = strlen($str);
  6.   for ( $n = 0; $n < $len; $n++ ) {
  7.    $result .= str_pad(decbin(ord($str[$n])), 8, '0', STR_PAD_LEFT);
  8.   }
  9.   return $result;
  10. }

2) Next, separate that really long binary number into sections of 6.
Code Sample:
  1. $sectionsArray = str_split($templateID, 6);

3) Each section has to be flipped.  Since the sections are stored into an array, I figure passing by reference to "string-flip" each section was the most efficient route:
Code Sample:
  1. foreach($sectionsArray as &$value){
  2.     $value = $this->flip($value);
  3.    }

Code Sample:
  1. function flip(&$str){
  2.   //flips a string "abcd" -> "dcba"
  3.   return strrev($str);
  4. }

4) With each section flipped, need to "glue" everything back together.
Code Sample:
  1. $implodedBinary = implode('', $sectionsArray);

5) Easy so far, right?  Yeah, here's the hard part.  Now we have to "read" the string.  I decided that as I "read" the string, I'd go ahead and do the lookup of each code.  I basically did this with 2 arrays, one to hold the decimal value (binary -> decimal convert each part), and an associative array to hold the name of the category (key) and the looked up name of the decimal code (value).  How it broke down:
Template type: 4 bits
Template version: 4 bits

Profession bitlength: 2 bits - result is y=(x*2+4)
Primary Profession: y bits
Secondary Profession: y bits

Number of Attributes: 4 bits - result determines how many times to 'loop' through the next part
Attribute bitlength: 4 bits - result is n=x+4

(start loop)
Attribute id: n bits
Attribute points: n bits
(end loop)

Skill bitlength: 4 bits - result is k=x+4

(start loop, 8 times, one for each skill slot)
Skill id: k bits
(end loop)

Trailing: x bits - unimportant, can safely ignore these

So that template code we started off with?  By following each step, we'll end up with decimal values, which every step can be viewed through the above link.

guild wars,


Dual Boot Mint 12 and Windows 7 on MST G

Mainly putting this here for my own reference, since I posted it on another site, and promptly forgot where it was.  Some newer hardware does not support the default Ubuntu/Mint installation, and requires some flags in order to get it work.  This was the case for my MSI GT780 DXR laptop, which I use for work (mint 12) and home (win 7).

I got it to load Mint 12 off the DVD. Mint 10 worked, but drivers were a big pain to deal with, and newer distros automagically have them.  Note that this is from straight out of the box hardware.

First, remove any former raid setup, and install Windows 7 first.

1) After BIOS post, press CTRL + I to enter RAID BIOS
2) press 2 to delete RAID
3) press 5 to exit
4) enter system BIOS (delete key) and setup SATA from RAID to AHCI
5) press F10 to save and exit
6) (re)install Windows

Next, install Mint 12.  This is sort of a carpet-bomb approach as regards to the settings, so I know a majority of these are pointless, but here's what I had set for the boot from DVD to work (I assume this works for the newer Ubuntu distros as well, since Mint is built on it):

1) BIOS:
USB Options:
Legacy USB: AUTO
XHCI: Disable
EHCI: Disable
2) Boot off CD, press any key to stop the 10 second countdown. Press TAB, and change the following:
REMOVE: quiet splash (optional)
ADD (before the --): noapic noapci nomodeset acpi=oldboot

I have a suspicion it's just the "acpi=oldboot" that is the magic fix on this one, so someone with more knowledge can chip in at this part.

I can confirm that wireless ethernet adapter AND the webcam work for the install.


Booting into it after install:

uhhh...new problem. Boot fails...not surprised. We need to edit boot options.

Boot normally, and it fails, so you'll have to edit the boot command. Fun stuff:

1) At boot menu, press "e" to edit the boot command.
2) Go down to where it says "linux /boot/vmlin"
3) Remove "quiet splash" (optional)
4) Add "noapic noapci nomodeset acpi=oldboot"
5) press F10 to boot into it. The first time I did this, it actually froze while booting. Did it again and it worked (I might have misspelled something first time)
6) Now you're in!
7) Change the boot permanently: Open Terminal
8) sudo bash

Now, at this point you can edit the existing boot, or make your own script. I made my own since I'm a noob and can't comprehend the existing boot scripts yet. All I did was copy the innards from the boot menu, and added the working options.

9) gedit /etc/grub.d/01_gt780dxr (or whatever your favorite editor is :P) (you can change the 01 to any number to change the ordering - with it at 01, it comes before the other options in the boot menu)
10) SCRIPT: ("simply" copy what is there in the other script when you press 'e' on boot menu - we're just adding the other options in the linux boot line)
#!/bin/sh -e
echo "I hope this works"
cat << EOF
menuentry "Mint 12 - MSI GT780DXR boot options" {
set gfxpayload=$linux_gfx_mode
insmod gzio
insmod part_msdos
insmod ext2
set root='(hd0,msdos5)'
search --no-floppy --fs-uuid --set=root c876a7ef-2ae7-4492-a481-c5150cd69408
linux /boot/vmlinuz-3.0.0-12-generic root=UUID=c876a7ef-2ae7-4492-a481-c5150cd69408 ro noapic noapci nomodeset acpi=oldboot vt.handoff=7
initrd /boot/initrd.img-3.0.0-12-generic
11) chmod +x /etc/grub.d/01_gt780dxr
12) update-grub
13) exit
14) reboot and pray it somehow works...oh wow it worked

Now I hope some linux script god will reply and tell me I did this all wrong and post an easier way to do this

mint 12, ubuntu, windows 7, dual boot, gt780, msi,


Dat Sass

So yeah.  You ever have a moment like the following?

Last week, I went to the used bookstore, and while waiting in the checkout line, there was a cute blonde at the register.  Nice body, and really tight pants.  Short blonde hair that's styled really cute.  You're like "alll riiiight".  

And then he turns around.

Note: I am a heterosexual male.



Custom BURG Background

Might as well throw this one in the mix.

Ok, no walkthrough for installing BURG, so tough...plenty out there.  HOWEVER, I wanted a custom background for the "radiance" theme I was using.  Sure, can customize a new theme entirely, but the look and feel of radiance is good enough...just needs a new background.

1) Terminal, need to "explore" to the location of the current background...
gksu nautilus

2) Go to
and open up the theme you want to change the background for.  In my case, this is "radiance".

3) Open up background.png, and mess around with that.  Ideally, you want the same "details" as the existing image so as not to have a weird, distorted image:

4) That's it!  Reboot and be surprised at how easy that was.

burg, linux, mint,


Change Login Backgrounds

Man, I'm in a customizable mood today.  Sigh.  I need a Mac just so I can cover all 3 bases.  For now, here's the only 2 I use.

Mint 12:
Code Sample:
  1. sudo gedit /etc/lightdm/unity-greeter.conf

Change the line that says "background" to a file location where your picture is.  I tried with a PNG, but it didn't work, so stick with JPG:
Code Sample:
  1. #background=/usr/share/backgrounds/linuxmint/default_background.jpg  //old background
  2. background=/home/sykohpath/Pictures/mikulogin.jpg
  3. logo=/usr/share/unity-greeter/logo.png

Logout-login, and you should see your background.  If it's a black screen, check your file location and letter casing.  I'm sure Image size doesn't matter - I'm using a 1920x1080; the default is 1920x1200, so I think as long as it's bigger than your screen, it'll fill it properly.


Windows is harder than Linux...nice!

Windows 7:

1) regedit

2) HKEY_LOCAL_MACHINE, select Find

3) Search for OEMBackground

4) If the key doesn't exist, create DWORD with the name OEMBackground.  Change the value from 0 to 1

5) Open Explorer, and go to

6) Make a folder named "info", and in that folder, make "backgrounds"

7) Copy-paste your background in here, and rename it:

NOTE:  File size must be less than 245kb.

8) That's it! AMAZING.  Log off and you'll see it.

There's also programs that can "automatically" do this for you, but come on!  where is the fun in that?

login background, mint 12, windows 7,




heptadecagon, with a feline twist.

heptadecafelinagon, sss,


Auto-install Important Updates

I'm one of those people that like to have everything updated every day.  So, every day when I log-in, I run Update Manager.  Well, that's freaking annoying, why can't it run automatically?  CRON JOB TIME.

First, get this through Software Manager, or whatever you use.  The package is called:

Next, open terminal...well, open this biglong filename:
Code Sample:
  1. sudo gedit /etc/apt/apt.conf.d/02periodic

And cram all this stuff inside:
Code Sample:
  1. # Enable the update script (0 = disable)
  2. APT::Periodic::Enable "1";
  3. # Get package lists every X days - apt-get update
  4. APT::Periodic::Update-Package-Lists "1";
  5. # Get upgrades every X days - apt-get upgrade --download-only
  6. APT::Periodic::Download-Upgradeable-Packages "0";
  7. # Clean every X days - apt-get autoclean
  8. APT::Periodic::AutocleanInterval "0";
  9. # Allow unattended script to run
  10. # Requires the package “unattended-upgrades” and will write
  11. # a log in /var/log/unattended-upgrades
  12. APT::Periodic::Unattended-Upgrade "1";

Change how often you want to update...heck, just read the comments, it's pretty self-explanatory.

Next, confirm the settings for what you actually want to download.  
Code Sample:
  1. sudo gedit /etc/apt/apt.conf.d/50unattended-upgrades

My default didn't grab "stable", so I had to add it at the top:
Code Sample:
  1. // Automatically upgrade packages from these (origin, archive) pairs
  2. Unattended-Upgrade::Allowed-Origins {
  3. "${distro_id} stable";
  4. "${distro_id} ${distro_codename}-security";
  5. // "${distro_id} ${distro_codename}-updates";
  6. // "${distro_id} ${distro_codename}-proposed";
  7. // "${distro_id} ${distro_codename}-backports";
  8. };
  9. // List of packages to not update
  10. Unattended-Upgrade::Package-Blacklist {
  11. // "vim";
  12. // "libc6";
  13. // "libc6-dev";
  14. // "libc6-i686";
  15. };
  16. // This option allows you to control if on a unclean dpkg exit
  17. // unattended-upgrades will automatically run
  18. //   dpkg --force-confold --configure -a
  19. // The default is true, to ensure updates keep getting installed
  20. //Unattended-Upgrade::AutoFixInterruptedDpkg "false";
  21. // Split the upgrade into the smallest possible chunks so that
  22. // they can be interrupted with SIGUSR1. This makes the upgrade
  23. // a bit slower but it has the benefit that shutdown while a upgrade
  24. // is running is possible (with a small delay)
  25. //Unattended-Upgrades::MinimalSteps "true";
  26. // Send email to this address for problems or packages upgrades
  27. // If empty or unset then no email is sent, make sure that you
  28. // have a working mail setup on your system. The package 'mailx'
  29. // must be installed or anything that provides /usr/bin/mail.
  30. //Unattended-Upgrade::Mail "root@localhost";
  31. // Set this value to "true" to get emails only on errors. Default
  32. // is to always send a mail if Unattended-Upgrade::Mail is set
  33. //Unattended-Upgrade::MailOnlyOnError "true";
  34. // Do automatic removal of new unused dependencies after the upgrade
  35. // (equivalent to apt-get autoremove)
  36. //Unattended-Upgrade::Remove-Unused-Dependencies "false";
  37. // Automatically reboot *WITHOUT CONFIRMATION* if a
  38. // the file /var/run/reboot-required is found after the upgrade
  39. //Unattended-Upgrade::Automatic-Reboot "false";
  40. // Use apt bandwidth limit feature, this example limits the download
  41. // speed to 70kb/sec
  42. //Acquire::http::Dl-Limit "70";

Aaaaand you're all set.  So now I can simply log in and magically...not manually update every dang thing.

ubuntu, mint 12, linux, auto install, updates,



Linux Mint 12 Auto Mount Drive

Here's how to automount a drive:

1) First, open up terminal, and determine what type of drive format it is:
Code Sample:
  1. sudo blkid

With a second hard drive, it'll most likely be /dev/sdb1 or something around there.  If you've labeled your drive, chances are it'll say LABEL="your label".  Note the TYPE="xxxx", which will determine which of the following you'll use.  In my example for my secondary hard drive:

/dev/sdb1: LABEL="Storage" UUID="92DEADA8DEAD8557" TYPE="ntfs"

2) Now you'll make your directory for your new mount.  I like to keep the name of the folder the same as the drive label, but you can name it whatever you want.

Code Sample:
  1. sudo mkdir /media/Storage

3) Man this is so hard.  Now open up your fstab...

Code Sample:
  1. sudo gedit /etc/fstab

4) And depending on your drive type, follow only the appropriate one:

*NOTE* It doesn't show up correctly here, but there is a tab where every space is.  This is important!  Copy-paste from here won't give you exactly what you need, so simply delete each space and TAB once.

/deb/drive     /media/location     type     defaults     0     0

Also, make sure you replace "/dev/sdxx" with your drive (such as "/dev/sdb1") and the "/media/xxxxx" with your same directory name you did above (such as "/media/Storage").

Code Sample:
  1. /dev/sdxx /media/xxxxx ntfs defaults,umask=007,gid=46 0 0

Code Sample:
  1. /dev/sdxx /media/xxxxx vfat umask=0000,uid=1000,gid=1000,auto,rw,users 0 0

Code Sample:
  1. /dev/sdxx /media/xxxxx ext3 defaults,noatime 0 2

Note: If you ever remove/comment out this line in /etc/fstab, the drive will be stuck in "read-only" mode.

ubuntu, mint 12, linux, auto mount,


April 1st

WELP.  On New Years Eve, I was in bed by 8pm.  April 1st?  Here it is 1am, and I'm still up.  YAY.

Normally (?) I'd screw up my site on the 1st, or make some random post on here about how I'm joining a monastery or something way out there like that.  This time?  meh.  How about I just sleep this one off, since I have a full-on cold right now.  Yeah, body is messed up all over, which is partly why I'm up at 1am.

Now that I think about it, I should have made a list of what I've done over the past years.  ooo...actually can't remember what all I've done...that's sad.  I've had some good ones!  I promise!

blog, april 1st,



MyAnimeList Signature Generator

ok, time for a long script!

This generates an image that shows your recent viewing history on My Anime List.  Of course, it's all tuned for me, but it's all easily changeable.  I've seen a few signatures for MAL already, but I didn't like how they worked, exactly.  Some were just plain badly coded (not that mine is 100% perfect), so I decided to write my own (viewable here):

Code Sample:
  1. <?php
  2. /* -----------------------
  3. MyAnimeList.net User History Signature Generator ()
  4. ----------------------- */
  5. $url = "http://myanimelist.net/rss.php?type=rw&u=" . $_GET['u'];
  6. $file = fopen&#40;"$url", "r"&#41;;
  7. //store page contents into buffer
  8. if (!$file) {
  9.   //if we can't create a file MAL is down, so create appropriate message as image instead!
  10.   $image = imagecreate(600,110);
  11.   $textcolor = imagecolorallocate($image, 255,255,255);
  12.   imagestring($image, 5, 85,26, "Unable to connect to MyAnimeList.net", $textcolor);
  13.   exit; //should make this end prettier
  14. } else {
  15.   $buffer = "";
  16.   while (!feof($file)) {
  17.    $buffer .= fgets($file,1024);
  18.   }
  19.   fclose($file);
  20. }
  21. //strip special chars
  22. $buffer = str_replace("n", "", $buffer);
  23. $buffer = str_replace("r", "", $buffer);
  24. $buffer = str_replace("t", "", $buffer);
  25. //parse RSS tags
  26.   //grab all item things
  27. $pattern = "/<item>(.*?)</item>/";
  28. preg_match_all($pattern, $buffer, $match_items, PREG_SET_ORDER);
  29. if(!empty($match_items)){
  30.   $x=0;
  31.   foreach($match_items as $matches){
  32.    //[0] includes item tags, [1] does not
  33.    //title
  34.    $pattern = "/<title>(.*?)</title>/";
  35.    preg_match($pattern, $matches[1], $match_title);
  36.    //description to determine status
  37.    $pattern = "/<description>(.*?)</description>/";
  38.    preg_match($pattern, $matches[1], $match_description);
  39.    //date
  40.    $pattern = "/<pubDate>(.*?)</pubDate>/";
  41.    preg_match($pattern, $matches[1], $match_date);
  42.    //link
  43.    $pattern = "/<link>(.*?)</link>/";
  44.    preg_match($pattern, $matches[1], $match_link);
  45.    //grab only "watching"
  46.    if(strpos($match_description[1], "Watching") !== FALSE || strpos($match_description[1], "Completed") !== FALSE){
  47.     $anime[$x]->title = $match_title[1];
  48.     $anime[$x]->status = $match_description[1];
  49.     $anime[$x]->date = $match_date[1];
  50.     $anime[$x]->link = $match_link[1];
  51.     //optional formatting - will change "title - TV - Completed - X of Y episodes" to "title - X/Y"
  52.     $anime[$x]->status = str_replace(" of ", "/", $anime[$x]->status);
  53.     $anime[$x]->status = str_replace(" episodes", "", $anime[$x]->status);
  54.     //Pre PHP 5.3
  55.      $before = explode(" - ", $anime[$x]->title);
  56.      $anime[$x]->title = $before[0];
  57.      $anime[$x]->status = strstr($anime[$x]->status, " - ");
  58.     //PHP 5.3 only
  59.      //$anime[$x]->title = strstr($anime[$x]->title, " - ", true);
  60.      //$anime[$x]->status = strstr($anime[$x]->status, " - ", false);
  61.     $anime[$x]->status = str_replace(" - ", "", $anime[$x]->status);
  62.     //increment object counter
  63.     $x++;
  64.    }
  65.   }
  66. }
  67. $image = imagecreate(600,110);
  68. $background = imagecolorallocate($image, 0,0,0);
  69. $bgtextcolor = imagecolorallocate($image, 50,50,50);
  70. $textcolor = imagecolorallocate($image, 200,200,200);
  71. $linecolor = imagecolorallocate($image, 255,0,0);
  72. // ---- scrambled text background ---- // (OPTIONAL)
  73.   $characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~!@#$%^&*()_+-=`[]{}:;'|,.<>/?";
  75.   for($vcntr=-1;$vcntr<60;$vcntr+=2) {
  76.    for($cntr=-1; $cntr<160; $cntr+=2){
  77.     $letter = substr($characters, rand(0,(strlen($characters)-1)),1);
  79.     $spacing = (rand(1,10)+($cntr*4));
  80.     $vspacing = (rand(1,10)+($vcntr*4));
  82.     imagestring($image, 5, $spacing, $vspacing, $letter, $bgtextcolor);
  83.    }
  84.   }
  85. // ---- logo image merge ---- // (OPTIONAL)
  86.   $logo = @imagecreatefromgif("http://www.sykohpath.com/images/sykohpath.gif");
  87.   if($logo){
  88.    //logo was loaded
  89.    imagecopymerge($image, $logo, 15, 12, 0, 0, 50, 50, 100);
  90.    imagerectangle($image, 15, 12, 65, 62, $linecolor);
  91.   }
  92. // ---- generic linkback ---- // (OPTIONAL)
  93.   imagestring($image, 4, 485, 90, "sykohpath.com", $linecolor);
  94. // ---- anime title status lines ---- // (REQUIRED)
  95.   for($i=0;$i<5;$i++){
  96.    if(!empty($anime[$i])){
  97.      //readability lines
  98.     imagerectangle($image, 85, ($i*16)+25, 580, ($i*16)+25, $linecolor);
  99.     imagestring($image, 5, 85,($i*16)+10, $anime[$i]->title, $textcolor);
  100.     imagestring($image, 5, 400,($i*16)+10, "(" . $anime[$i]->status . ")", $textcolor);
  101.     imagestring($image, 5, 480,($i*16)+10, date("Y M d", strtotime($anime[$i]->date)), $textcolor);
  102.    }
  103.   }
  104. // ---- border ---- // (OPTIONAL)
  105. imagesetthickness($image, 2);
  106. imagerectangle($image, 0, 1, 599, 108, $linecolor);
  107. // ---- generate the image (finally) ---- //
  108. header( "Content-type: image/png");
  109. imagepng($image);
  110. // ---- free up resources ---- //
  111. imagecolordeallocate($linecolor);
  112. imagecolordeallocate($textcolor);
  113. imagecolordeallocate($background);
  114. imagedestroy($image);
  115. ?>

php, script, myanimelist, mal,



Black Rock Shooter - TV

The last episode of Black Rock Shooter (tv) aired, and goodness, quite a ride.

I absolutely love the "other world"; great visual effects surrounded by action and fantastic designs by huke.  I've actually ordered his art book, which should be here next week - it's full of BRS designs, and comes with a figma, too!  For the other-world part of the anime, it's 10/10.

Then there is the "real world" aspect of the anime.  In looking at nothing but the reality part, we're presented with hyper-emotional girls and an apparently sadistic counselor.  In the end, everybody is happy, but the presentation just seemed to drag down the quality of the story.  Just this part is a solid 6/10.   Yeah, it wasn't as enjoyable as it should have been.

Overall, the anime is a 7/10, whatever that means.  Since it's part of the BRS world, it's impossible to avoid comparison with the OVA; the same characters are used in the same setting, with a similar story.  The OVA focused entirely on the relationship between Mato (Black Rock Shooter) and Yomi (Dead Master), and I wanted to see more of the other characters within the world.  The TV series used the other characters, which I enjoyed, but the story in the OVA I felt was stronger overall.  The character designs had some minor tweaking, although Dead Master had more of a "wedding" theme with more green; the green is better, but the outfit in the OVA felt better (not to say I didn't like the TV version).

Again, I'm anxious for the art book to see if there are other renditions of the characters.

black rock shooter, tv, anime, review,




Current status: Episode 3/12

Just watched 3 episodes in a row, and was taken back by a rather nasty twist at the end of this episode.  

From the start of episode 1, we're presented with a "ghost story," and we quickly figure out what's going on.  All is fine and dandy in creepy-anime-world, and then...expectations are suddenly smashed with a hammer.  Seriously.  It was surprising enough where I had to come to this blog and do an actual anime post about it.  No spoilers, of course, but I have to say, "Another" is a rather good anime, at least for the first 3 episodes.

anime, review,






Pest-Control "War Stories" Series, p8

I was once a professional exterminator (Terminix). At the time, we had these cans that would shoot really fricking far, and "insta-kill" wasps. It didn't have any staying power, however. It would just soak a nest, but of course, wouldn't kill any wasps inside (plus there's ones "out" that would eventually come back).
So yeah, that would cause us to get called out multiple times if we only used the spray. Here's what we would have to do. Each of us had a de-webber that would extend like, 10' or so. We also had our normal pressure-canister of bug-killer (the stuff that is preventative and lasts 30+ days or so). Using the dewebber, we'd knock the nest down, and then simply spray using our preventative on where the nest was. Wasps return home based on memory, so they'd eventually land in the preventative spray and die that way.

------- vietnam flashback follows ------------------

Biggest nest I've had to deal with was larger than a volleyball, and was on a mobile home. There was no "cover" for me, and there were an assload of wasps just flying around. Our dewebbers were bright yellow, and made a great target for wasps (better than us!). Every time I got close to this nest, however, wasps would not only go after my distraction-wand, but after me as well. So what to do if I can't get close to it?
I know that wasps need to have time to land before stinging. Also, we were required to wear long-sleeve shirts and pants. Our only exposed skin was hands and head. Most of us wore hats as well (Everything company-issued). I was a good distance away, so I just started running, with my wand joust-style up in the air aimed at the nest. The instant I hit it, a HUGE BURST of wasps exploded from the nest as it fell, and I swung my dewebber away from me as I turned and ran the fuck away from the huge mess I just caused. Of course, I was dragging the wand behind me in the grass, looking for wasps on my hands and trying to pay attention to anything in my hair or on my face. Once I was far enough away, I dropped the wand and checked my clothes for wasps. None of the bastards were on me, thank goodness. There were quite a few attacking the yellow tip on my dewebber, but those were easy to brush off into the grass. Looking back at the mobile home, there was a really large cloud of pissed off wasps I now had to deal with.

The majority of wasps are pissed off for not very long before they determine there's no threat nearby (cocky bastards), so I took this time to walk back to my truck, prepare my preventative spray, and then arm myself with one can in each hand of the insta-kill spray. I essentially respecced from melee to ranged.

The thing about the insta-kill spray, is that it knocks wasps out of the air immediately. They fall to the ground and sting whatever is in reach, before dying within two minutes after contact. This was a day I was grateful for wearing (steeltoed) thick workboots. Also, I taped the cuffs of my pants to my boots, to prevent any crawlers from getting up there (not likely with the spray, but why chance it).

So, I walked toward the cloud, guns blazing. My first spray knocked directly into the middle of the cloud knocked down a visible hole, so I just went all-out. I used up 6 cans of the stuff before most of the cloud was gone. The grass looked like it was moving; so many dying wasps exercising their stingers for the last time in their lives. The still active nest, lying on the ground was the final target, of course. Amazingly enough, it still held its round shape, except for a nice dewebber-sized dent in the side. Apparently paper doesn't hold too well to vinyl. I soaked that motherfucker until it was flat.

I finished off the massacre by spraying preventative spray all over the area where the nest was. Mind you, the whole time this was happening, wasps were returning from their scout-missions, wondering what happened to their castle-sized nest. Whenever a nest is knocked down, wasps will try to rebuild on the same spot, so eventually a nice gathering of wasps were getting covered in the spray. It's slow-acting, with the intent that the spray will "accidentally" be applied to other insect that touch each other. Good stuff, but damn it's slow.

The next month for my regular service call, I inspected the area. Not one wasp was in the area. Mission complete.

Also, still to this day, I have never been stung by a wasp or bee.

pest control, story, terminix,


Pest-Control "War Stories" Series, p7

Speaking of memorable stories:

One customer had a dead and just starting to rot squirrel in their driveway. They wanted me to dispose of it. I was brand-spanking-new on the job, and apparently we weren't supposed to take care of things like that. Oh well, too late. I put it in a trash bag and threw it in the back of the truck.

At the end of the day, I remove the bag from my truck, and put it in the garbage can back at the office. I mean, this is a special garbage can made for disposal of hazardous things, so I figure that's the best place for it. I go on my happy way, thinking job well done.

Now, about a month later, during an office meeting, the branch manager starts telling a story about how the higher-ups decided to make a surprise visit at the branch, and how on that same day "someone" had put a dead squirrel in the garbage can. Needless to say, they weren't pleased (regardless, we were still the best branch in the region).

For my boss's birthday, I got him an emperor scorpion (big black thing). He fed it black widows. He also had a bug container that he kept black widows in (he collected them from jobs).

Oh yeah, one of his stories. He and his boss had to clear out a crawlspace of this really freaking old house. This place was bug-heaven, apparently. For example, they found an old roll-top desk that was literally crumbling. On opening it, a bajillion baby/spiders were exposed. Anyway, they had to clear out the crawlspace - so they remove the door to it (was inside the house I guess), and there literally wasn't a clear spot to go down; the entire entrance was webbed and had black widow spiders living under the door. My god, if I had pics of this, I would have so much karma...anyway...

They had to put on full-body suits, and just...well, jump down into it. I think they did the pyrethrin dust treatment - a machine that pumps out a ton of the dust, and kills on contact (preventative mainly). This thing pumps out a TON of dust. What it does, in short, is affect the nervous system, causing bugs to become overactive - that's a symptom that happens within seconds. So yeah, they're down there, and turn this thing on...basically have to crawl around the crawlspace and dust every square inch of it. So now you can imagine an innumerable number of pissed-off black widows crawling over these two guys. Fortunately the suits are pretty thick.

As you can imagine, when they finally finish and get out of the crawlspace, they're covered in black widows. He didn't say how they got them off, I assume their massive balls crushed all the spiders under their weight. Anyway, one of them did manage to get under his full-body suit and bite him on the shoulder. Left a nice big black-ish mark.

pest control, story, terminix,


Pest-Control "War Stories" Series, p6

Let me say this...avoid Mexican and Chinese restaurants/markets lol.

Ok here's by far the worst one I've ever treated:

Now, I was a tech in a major-ish city, and with that comes neighborhoods with the standard good and bad areas. The reason I say this, is because it seems like the health department has never heard of these mom and pop places. I swear I'm not racist, but this is just how things were. The Chinese and Mexican restaurants were filled with a grease-like substance. I mean, it coats the walls. The longer the establishment has been operating, the thicker the stuff on the walls is. I'm not talking about chains, mind you; they at least have some sort of cleaning schedule (not to say they're immune from infection, it's just...less). This is of course mainly noticeable in the kitchen area, so if you can somehow sneak into one, run your finger on the walls and you'll see what I'm talking about.

There's a Mexican food market in a bad part of town that has just signed a new pest control contract with us. I arrive there, and no one speaks a word of English. From high school, the best I can say is "yo hablo poco espanol" (high school also taught me Spanish swear words, but those weren't applicable on the job - one time I said "el cucaracha es muerte" and got a funny look). So anyway, the people there point at the walls, and it's obvious what I need to do. I need to burn the motherfucking place down. Unfortunately, that kind of treatment is not allowed by our branch.

Let me point out a tip about cockroaches: they like darkness, dampness, and heat. If you see a roach in the light, more than likely it can't find a hiding place - meaning that all the hiding places are already full of cockroaches. This place had roaches scattered all over the walls (they like to hang out around the top edges of rooms, and congregate into groups). One of the best prevention tips is to keep food in sealed containers, and to make sure there are no water leaks anywhere. There's a reason I mention this. Also, a common theme I see with (cheaper) Mexican and Chinese places is that they build furniture-things with these massive gaps at the joints. Whenever you visit a restaurant, look at where the wood meets, and you'll see what I'm talking about. You'll also spot roaches in there, too.

Ok, this is a Mexican market. It's in a strip-mall, so it's likely that any infestation has already reached the other neighboring stores. It's literally impossible to stop this infection, but I won't be told I didn't try. They have exposed food lying on counters. I'm talking meat. They leave it sitting there all day, and overnight, exposed. In the back of the store is a very small bathroom with a toilet, and water dripping from some random spot in the wall, with water on the floor. Oh yeah, all the walls in the store were half-finished, meaning that the front of the store had sheetrock, but there was nothing behind the walls (except the normal side walls and back of the store). There is a drop-ceiling, with about a foot of space between that and the real ceiling. So we're talking hiding spots everywhere.

They let me into the store, and expect me to treat everything like it is. The chemical we used at the time for roaches was on-contact nasty stuff. We had to wear respirators and gloves, since any contact with skin was bad. Let me say it would be illegal for me to treat around exposed food and water sources. I turn around and try my best to tell them that food needs to be covered and stored...fast forward an hour or so, and everything is finally set - food has been moved into plastic bins (lol), and water has been cleaned up in the bathroom. I can finally get to work...mind you, the store closed at 11pm. The employees wait out front for me to finish since they have to lock the door when I'm done.

So yeah, I find where the main counter meets the wall, and take a flashlight to the crack. Queue the part where several roaches freak out and try to escape the light, which consists of running out into the exposed light. Yep, this is where I'll start. I shoot into the crevice, and flood out an innumerable amount of the little bastards (oh yeah, another thing I forgot to mention - we needed to tuck our pants into our boots and tape them to prevent roaches getting in). There's roaches crawling all over my legs almost immediately after I spray just this one area. Spraying that one crevice also flushed out a black stream of roach-shit - normally roach droppings are around the size of a grain of sand - this was...black colored pest control fluid.
From there, I go around the perimeter of the store, hitting the edge of the floor and ceiling. The drop-ceiling part needed pyrethrin dust (since it was a hollow space), so I needed to put a puff up in there at every ceiling tile. By this time, the walls were literally moving. Roaches were covering all of the plastic sheets covering the store's product (also dropping from the ceiling). I go behind the fake-ish walls, and by this time, they are nothing but moving masses of black. Normal treatment is just perimeter, but I switch the sprayer to "fan", and just crop-dust the walls. For some reason, the bathroom didn't have any roaches in it by the time I got that far (besides the ones I brought in that were crawling on my clothes). Also yeah, I had to constantly flick them off my neck and face. Thank goodness they don't bite. Also, every step I was taking had at least a "crunch" or two.

I end the treatment, and go out the front door. The lady sees that I'm finished, and walks straight in the store, all the way to the back, and turns off the lights. She then walks through the store through the front door and locks it. Even though she has just walked through the seventh level of Hell, she acts like it's just a standard stroll in the park.

They called me back the next night...its impossible to explain that these treatments need time to work, but meh, it's a good excuse to survey the damage. Dead roaches have been swept into piles along the walls. Food was back on the counters, still exposed, with dead roaches lying on the other tables and shelves in the store. The back area wasn't even bothered with, so it was just a carpet of crunchiness. The workers had moved in stock boxes into the back, and apparently just stacked them on the roach-carpet. I opened one of the boxes, and sure enough, it was infested. Welp, I tell them that they need to put the food away properly, and then do a light perimeter treatment...even off that, roaches were still crawling out of the cracks. I finish up without any other issues.

A few days pass, and I'm meeting with my boss. He tells me that they had called the next night, and that he personally went out there to see what was going on. He could tell I treated everything, and noted that the roaches were still left all over the place, without proper cleaning. He said he got the hell out of there, laughed, and called the Health Department.

I drove by there a few days later out of curiosity and they were closed.

pest control, story, terminix,


Pest-Control "War Stories" Series, p5

I mentioned we service in the winter...heh can't believe I forgot about this one.

Randy had to occasionally ride around with other techs as part of his training - mainly to observe and write up reports about how we did service. I pick him up at 6am, and only have 4 stops on my list for the day. Shoot, we'll get this shit done in an hour, tops. The thing is, these stops are up in the mountains. As I'm driving up there with him, it starts to snow.

The trucks we are assigned are not 4wd. They're Ford Rangers from 1980 something. They have no power to them at all. Fortunately we carry chains, but we figure we'll put them on at the first stop (since we're almost there). Well, suddenly I have to drive up a steep hill in the snow, and make a left onto a sideroad up the hill. There is quite a bit of traffic coming down the hill, so I take up the middle lane, and sit there with my signal on. A rather large semi-truck pulls up behind us, needing to turn on the same road. Finally, and opening, and I slowly hit the gas. We're not moving. The tires are spinning, but we're just not going. Randy hops out, gets behind the car, and starts pushing. It's successful, but, we're now in the middle of the opposite lane, and cars are trying to stop going down a hill.

Randy turns on his Hulk powers and pushes us into the side road - a sliding car nearly kills the poor man, but he's running and it barely misses him. He's yelling "GO GO GO" as I go on the side-road, and I see why; the road is covered in snow, and goes up another hill almost right away - if I stop, we're probably going to slide back into traffic. So yeah, I'm trying to build up speed, and Randy is running as fast as he can beside the car, trying to catch up and hop in. We barely make it up the hill, and he says to stop in order to put the chains on. We hop out and get to work on the chains. Randy stops and says, "I think you pissed off the truck driver." I look, and sure enough the semi driver is cussing and putting his chains on his semi in the middle of the street - there is no way he can get any traction to pull onto this side-road.

We finally make it to the first stop. This is in a neighborhood further into the mountains, and the snow has been continuing nonstop the whole time. Randy helps pull out some of the equipment, and we treat as best we can around the house. This whole time we can't believe how awful this is going. I mean, it's really coming down. At the third stop, the snow has built up to be as high as half the truck height.

The next two stops are similar, but nothing exciting happens - just an assload of snow, shoveling, and pushing a truck around (even with chains haha). It's around 6pm, and the last stop is back down at a low elevation - it's a rough drive going down, but I swear, the instant we drop below a certain point, there is NO SNOW, and it's literally 60 degrees. The last stop is a self storage spot, and Randy sits in the back with the sprayer while I drive around each building.

He marked my report "pass" with no other comments.

pest control, story, terminix,


Pest-Control "War Stories" Series, p4

So yeah, there were bad places, right? Here's the worst residence I treated. This is pretty bad, so advance-warning.

Part of my route covered a "not a safe part of town" residential area. At the time, I was a young, naive white kid in a pest control uniform. Scared shitless, I've been given this one-time-treatment contract for this...shack.

It's a duplex, but, one side doesn't have a roof. My contract is to treat the whole thing, too. I walk up, and this guy with no shirt, and this LARGE gauze bandage on his stomach answers the door. He doesn't answer it cheerfully. I say I'm here to treat for bugs, and suddenly he lights up and is the friendliest person one could ever meet. He calls his girlfriend(?) over to point out where they saw bugs (specifically roaches). This side of the duplex is fairly clean and well taken care-of, however, they have a teenage boy that "lives" in the other part of the duplex. I'm lead to a door that opens up into the other side.
When you open a door into a residence, you should not be hit in the face by the sun at noon. I give my best "wat" expression, and she motions me to go in. She won't enter the place, and for good reason. There's dog-shit all over the floors. Undisturbed dog-shit. Apparently the son had a pet dog, and the dog squat down wherever it felt like on the floor, and not the fenced in yard outside. I'm talking mine-field here.
The bedroom had a mattress with one yellowed sheet. I couldn't tell if that was the natural color, or the dog/owner had just simply pissed the thing all over and left it like that. That was all that was in the bedroom (besides dog-shit). I hope you haven't forgotten that I've mentioned there isn't a roof on this side of the duplex, because, it's still not there. This dude literally camps out under the open sky every night.
The kitchen had no doors on the cabinets. You know what? I literally can't describe anything else in this place, since there literally wasn't anything else in this place, besides missing doors, a mattress with a yellow sheet, and did I mention dog-shit?

I do a perimeter treatment, and do what I can as far as a normal treatment. I bait for roaches in the cracks, and finish up this side. Alright, got this done. I open the door back into the "civilized" part of the duplex, and the man that had the gauze on his stomach was now lying on the couch, gauze removed, and preparing a new pad. This guy had no skin on his stomach. A visible cut-square of flesh was just missing, and the fat-layer was just there, exposed to the open air (no blood, mind you). He started talking to me like nothing out of the ordinary was happening, "See any bugs?"

I finished treatment of the place, and the amusing part of it all? I saw 0 bugs in the entire place. I never got a return service call to the place either. I assume the dog-shit half of the duplex had scared away all the bugs, causing them to move into a better part of town.

pest control, story, terminix,


Pest-Control "War Stories" Series, p3

Ok, this one client was batshit insane. He lived out in the middle of nowhere, and his place was powered by generators. I mean, if you met this guy in real life, you wouldn't suspect a thing, but the instant you saw his mobile home out in the middle of nowhere, the first thought that would cross your mind is "I'm not leaving this place alive".

He had this rusty barb-wire fence surrounding his property, and he had signs saying something like "You're under surveillance" (memory is foggy on what exactly they said - they were generic warning signs of "keep out", but with more conspiracy theory themed wording). There was a weedy, narrow dirt path leading up into his place...oh yeah, he had one of those large metal swing gates. He was nice enough to already have the padlock unlocked, so I could open it. I didn't see any cameras, but his place was easily visible from the front gate. I mean, if the guy had a sniper rifle (assuming he did haha), he could easily get a clear shot straight down to whoever was messing around with his gate. This was one of the times I was glad there was a giant "Terminix" logo plastered on the side of my truck.

Oh yeah, another worry? No address letters. I mean, at the time, there was no GPS or Google maps. We had these large map-books of the area, and the best detail they got was just a scribbly line with the name of the street. You want "Dead Bodies LN"? That's in Square I-4. Good fucking luck to you if you find it, because the map drawer didn't even bother driving up that road. So yeah, I had to "guess" that I had the right place, most of the time for first visits. After that, it was easy to spot-memorize locations.

So I drive up there, knock on the door, and the guy answers. Really nice guy, and kinda nerdy. He invites me in, and there's hardly anything in the place. I mean, he had these HUGE antennas on top of the place, and of course, crazy-person signs all around the outside, but the interior was spotless (everything was decorated brown...linoleum floor, panel walls, countertops). No dogs, which I kind of expected from these out-of-the-way places. He gives me a quick tour, and in one bedroom, he has this massive server room (has about 7 computers under a fold-out dining table, with 3 monitors on top, also has wires running up to the antennas). Never bothered to ask what it was (fearing for my life as it is at this point), but yeah, suspicious much? You bet. The guy didn't even have a tv in his place.

So yeah, getting to the actual pest story. He wants a normal treatment, but he wants me to get rid of a possum living under his mobile home. At this point, who am I to say no, even though this was after I found out we don't deal with "animals". I figure I can scare the thing away and then tell the guy to seal up any holes in the skirting.

He takes me out back, and he has this fire-pit made of cinderblocks. Apparently he burns his trash in it. Anyway, what he did was, he came home one night and heard something rustling in his garbage can on his back porch. It was empty at the time, so he lifted the lid, and there was a possum at the bottom, just staring up. The crazy son of a bitch lit his fire-pit and dumped the possum in there. Of course, the animal didn't like that one bit, so it jumped out and ran right under his house, still having bits of flaming paper stuck in its fur. He said he's left the can open several nights since, but it hasn't jumped back in, even with "bait" (who can blame it haha).

He removes a panel, and I crawl under - immediately, I see light coming through a hole in the skirting, and make a note of it. I crawl around, and shine my flashlight, but I never do find the damn thing. I crawl back out, and inspect the skirting around the outside. There's scratchmarks and "chewing" at every corner of the place. I assume this is an outpost that gets attacked by zombies every night, by the looks of it. I tell him the problems, and he agrees to fix it.

The guy initially signed a 1 year contract, but after my visit, he immediately cancels his service. He never paid his bill, either.

pest control, story, terminix,


Pest-Control "War Stories" Series, p2

Most embarrassing story? Yep, here it is:

Did I mention I'm afraid of heights? Yep, I was, and still am. I honestly can not for the life of me remember why I had to get up on this client's roof, but I did. It was a two-story house, so there was no way I could even get to the edge of the roof without panicking, and had to basically just sit on the roof and scoot around.

Wait I remember now, they had a wasp nest in one of the air-vent thingies up on the roof. The bastards were flying down into it and were somehow getting into the house, but the made a nest in the top outlet of it. Yeah, painfully easy to treat. Shoot em and run. Of course, it was toward the peak. The whole time, I'm scooting around on my ass, mind you.

The thing about roofs is, shingles aren't smooth. They're rough. So, scooting around on them, that's gonna cause some fabric issues. I don't even notice that there's an issue until I give the customer the invoice, walk away from her to my truck, and sit down. I feel the truck seat, so I reach down, and feel bare-ass skin. Customer never said a thing for following visits (however, she did say I was the best tech they ever had, since I got rid of the wasp problem haha).

One of the tips is to keep spare clothes in the truck, due to unforseen situations. Yep, this was a situation, so I drove off, parked the truck in a non-public-visible area, and changed my pants in the truck. The roof had shaved some skin off as well, so I had a nice scar that lasted quite a long time as well.
Ok related, and I swear this is just a myth-story told to pest control tech-noobs:

Tech answers the door, and a lady answers the door wearing nothing but a robs / nothing at all. Fucking lies, that never happened on my route.

We're not allowed to take tips, by the way. However, if you offer us water or food, you're basically Jesus in our eyes from that point forward. Skimpy clothing would probably not be minded as well, but I'm still convinced that's made up.

pest control, story, terminix,


Pest-Control "War Stories" Series, p1

On Reddit (a place full of viruses and pornography), I had posted some "war stories" of when I worked as a Pest Control Technician for Terminix.  They got an amazing amount of positive responses, so I'm putting all of them up here for the 2-3 spambots that hit my site to have something to do.  Enjoy!


I used to be a pest control tech, and Mexican and Chinese restaurants are the worst, especially the "cheaper" quality ones. If the arcade machine is any indication of the restaurant, you probably shouldn't read this if you want to continue eating there without feeling paranoid and sick.

There's usually a layer of "grease" on the walls, especially in the kitchen. If you go into the bathrooms, you'll probably notice lack of care and/or unfixed water problems. One common trend is that there are a ton of dark crevices. You know what likes grease, water, and dark crevices? Roaches. The next time you're there, keep an eye out, and see if you can see in the cracks. What's even worse, is if you spot any roaches out in the light, especially along the top edge of the walls against the ceiling (if you notice groupings of black specks, that's roach shit), that means that the roach has ran out of hiding spots, since they're already filled with roaches. They like dark, damp, and heat. At night, they travel all over the restaurant, getting into any exposed water and food.

A fun trick you can try to check on your local restaurant - buy some small glue traps (it'll say for mice and whatnot). Set one up in a hidden spot, like behind the toilet. If you can go there the next day and check it, it should be clean. If there are any roaches (or even mice) on it, GTFO. Now, if you leave it there a week or a month, it should catch spiders and possibly ants...those aren't bad, and more common than you think (spiders are good!). It should NOT be full of roaches.

In fact, at the table you sit at, take a look under the table. There should be no roaches...if there are, there's a rather severe case of infection at the place. You can also check around the booth seats, and any of the cracks in the shoddy craft booth dividers. Roaches tend to stick by food sources, so they'll be in the kitchen, and to a lesser extent, bathrooms. If they're out in the seating area, that's really bad, I mean, it means they are overflowing from the kitchen. It could also mean that they don't clean the dining area very well - common occurrence to have them in the dining areas of buffets.

pest control, story, terminix,






Spring Forward

Time experiment complete.  err..."Time Change" experiment complete.  When the time change occurred in fall, I decided to adjust my clock, yet keep waking up and going to work at the same "time" my body was on (work from 7 to 4 instead of 8 to 5).  I did this all winter.

As of this morning, since the time change "spring forward" hit, it means that it was a success; I went the whole winter going to work at the "same time".  Here's what I learned:

1) It's fricking dark.  Like, pitch black.
2) People like to jog/walk wearing dark clothing during these times, and tend to do this on the busiest side of the road.
3) Leaving work at 4pm, it's still light out.  This avoids the "dark when I go to work, dark when I leave work" pattern.

blog, time change, complaining,


Site Todo List

New todo list...in fancy numbered code list, since I'm really happy how it works!

Code Sample:
  1. tag field for entries
  2. archive calendar
  3. auto-format anchor-href tags to support links.  Regex, probably.
  4. comments?  not sure I want to add that...

Hmm, that's it?



Handling Fake Tags

muahahah fixed it all...sorta.  dang, I can't read the form text now...whoops.

Ok, here's now to made "fake" bbcode tags, and autoformat numbered code lines (simply remove the Y in the tag, or make up your own tags to whatever you want):

Code Sample:
  1.     //fix html tags for specials
  2.      //faked bbcode
  3.     $text = str_replace("[Ycode]","<code>",$text);
  4.     $text = str_replace("[/Ycode]","</code>",$text);
  5.     $text = str_replace("[Yp]","<p>",$text);
  6.     $text = str_replace("[/Yp]","</p>",$text);
  7.     $text = str_replace("[Ypre]","<pre>",$text);
  8.     $text = str_replace("[/Ypre]","</pre>",$text);
  9.     $text = str_replace("[Ycopy]","©",$text); //copyright symbol
  10.     $text = str_replace("[Yreg]","®",$text); //registered symbol
  11.     $text = str_replace("[Ydeg]","°",$text); //degree symbol
  12.     //numbered code generation
  13.     $pattern = "/[Yncode.*?](.*?)[/Yncode]/";
  14.     preg_match_all($pattern, $text, $matches);
  15.     if(is_array($matches)){
  16.      if(is_array($matches[1])){ //means there are multiple matches
  17.       foreach($matches[1] as $value){
  18.        $replaceString = $value;
  19.        $codeLine = explode("<br>", $value);
  20.        $value = "<ol><label  Sample:</label>";
  21.        foreach($codeLine as $codeRow){
  22.         $value .= "<li><code  . $codeRow . "</code></li>";
  23.        }
  24.        $value .= "</ol>";
  25.        $text = str_replace($replaceString, $value, $text);
  26.       }
  27.      }
  28.     }
  29.     //finally, remove the marking tags!
  30.     $text = str_replace("[Yncode]","",$text);
  31.     $text = str_replace("[/Yncode]","",$text);

Code Sample:
  1. code {
  2. font-family: Courier, monospace;
  3. color:#000;
  4. /*font-style: italic;*/
  5. font-size:10pt;
  6. background: url("http://www.sykohpath.com/images/background.jpg") repeat;
  7. margin: 0px 0px 0px 0px;
  8. padding: 0px 0px 0px 10px;
  9. display: block;
  10. border:1px #c01 solid;
  11. }
  12. ol {
  13. background: #c01;
  14. overflow:auto;
  15. font-family:Verdana;
  16. text-decoration: bold;
  17. color:#eee;
  18. margin: 0px 10px 0px 20px;
  19. padding: 0px 2px 2px 25px;
  20. border: 1px #ffccee dashed;
  21. }
  22. ol li {
  23. background: url("http://www.sykohpath.com/images/background.jpg") repeat;
  24. font-size:10px;
  25. font-family:Verdana;
  26. text-decoration: bold;
  27. list-style-type: cjk-ideographic;
  28. }

php, tags, code,


SOPA/PIPA is not Mexican Food

Today is "black out websites to protest SOPA and PIPA".  

I'd do that for this site, but unfortunately, no one visits it.  However, this site IS IN VIOLATION because if you scroll down, you'll see a screenshot from World of Warcraft.  SOPA and PIPA would deem this "bad" and my website would be blocked from having anyone visit it (I could be fined and prosecuted as well).  How asinine is that?  Over a screenshot?  Sounds ridiculous, because it IS rediculous.  This would be a step backward in progress.

sopa, pipa, complaining,




time to start learning japanese again.  whee.



Memorial: Professor Stinker

I got our cat "Professor Stinker" 10 or so years ago.

He was walking out in the middle of the desert-area behind the condos, and one of our friends at the time found him. It was a pet-free complex, but screw that shit, we took him anyway. We never got caught the whole time we lived there.  Thinking he was abandonded, we put an ad in the paper to make sure he wasn't lost, and didn't find any "missing pet" posts for a few weeks, so we kept him.

He had a wonderful temperament, and was already fixed, too. We moved across country. He HATED to be in a cat carrier, so for the first 4 hours of the 2200 mile trip, he hurt his nose on the carrier until I said "let him out" so he could walk around the U-Haul cab. He loved it - he spent most of his time in my lap, and up on the dash in the window.

One of my favorite pictures is of him lying on the dash (the other favorite pic is of him sitting on the coffee table, during his "fat" stage). At the new place, he was a indoor/outdoor cat, since the place we lived at was a shithole, and had a door with a large hole in it (can't count how many possums I had to chase out through there). He fought with the local neighborhood cat (who later "adopted" us by laying on the couch when we came back from a vacation once - we called him "Evil Kitty"), until we got that cat fixed. Got divorced, and she kept the majority of material crap - stuff I didn't really care about anyway, except for a few sentimental things. I wanted a new start, but dammit, I was taking the cats. I even took the neighborhood cat.

I moved in with a friend (he had a cat too!), so we were up to having 4 cats in the house at one time. This place was more out in the woods, and a few times I took Stinker for "walks" with me out in the woods. Got a better job, and moved into my own place...he moved with me again, but I made him full-indoor cat. He still snuck outside whenever he could - if the door wasn't closed all the way (or still space while closing), he would run out. Heck, if the door wasn't latched properly, he could pull the door open and run out. Most of his days he was around me; if he wasn't in my lap, he was sleeping on the bed, or by my feet.

I got a better job, and moved AGAIN. He lived in the new, much larger house for about a month. Took him into a new vet to get his teeth cleaned. Brought him home, and he was howling, and could barely walk. Kept bringing him back to the vet, but everything "checked out fine". After a few days for the medicine to be completely gone, he didn't improve. In fact, he got worse. Apparently he had a stroke (was blind, and lost most of the use of his back legs), and must have had smaller strokes after.

By Friday (took him in for teeth cleaning on monday), he couldn't even stand or move his legs. He wasn't improving at all, so had to take him in to be put down. You bet your ass I stayed with him. Even though he couldn't see, I made sure he could smell me. He went to sleep as peacefully as he could. I had the option to keep his ashes, or to have them scattered. Since I found him out in the wild, no idea where he came from; I think it best to have him go a similar way.

cat, sad,


Happy Aexivile New Year!

2 days late...oops




site update

Small tweaks to this highly ignored site.  Graphics line up and generally "fit" the look of the site better.  Also, test.

blog, maintenance,



Yep: Ripped Bible




New Equipment

Put my vinyl logo on my snowboard helmet...I think it turned out nice!

snowboard, graphics, blog,



Happy Halloween by the way.  My costume is a "killer whale"...I mean, it's a hoodie with cut-out index cards stapled to it.  It got a good reaction haha - I'll post pics later (edit: blatant lie).

I purchased "Torchlight" on Steam for 20$ - wasn't going to do it, but I saw an article about how this company went out of their way to make one person happy over a problem.  Just off of that, I decided to give the game a shot.  I was pleasantly surprised!  I never would have paid attention (or cash haha) for this game if I didn't see that article.  20$ for a game is a steal to beginwith...Review: "Buy It"

blog, costume, game,



How's this for a work week?  Main server goes corrupt, and there's no backups.  Well, there's a backup, but it's from February.  Tech support for the system has been awful, overall.  From being put on hold for over 4 hours (well, was 6 hours if you include the disconnect), their phone had some horrid distortion so I could only hear half the things the guy was saying (not like it really mattered once he remote-accessed the computer), and well, they didn't solve any of the problems I called in for.  In short, I restored from backup, and then reinstalled a companion program.

So far it looks like it's working, but that remains to be seen.

I found a super-really-awesome deal on REI Snow equipment.  I got a helmet, goggles, and day pack for...100$ total.  Separately and not on sale, they total out to over 250$.  WIN.  

I joined Crossfit in order to "train" for my snow trip in 6 weeks.  I have the gear, now I just need to get in shape for it.  My biking for 5 months has been a nice increase in endurance, sure, but I need other attributes worked on besides legs.  

It's been 2 weeks that I've been in Crossfit.  Total Weight Loss: -4 pounds.  Yes, I lost -4.  I'll admit my diet hasn't been necessarily good.  However, I AM getting stronger because of Crossfit.  Already I'm noticing increased energy and mobility, as well as being able to tackle warm-ups better.  Last night was a leg warm-up...it killed my legs.  Tonight is for "the other half."  That's the thing with Crossfit - it's a full body workout, in that it nails every square inch of your body.  I mean, I can feel it in warm-ups.  The 5-warmup (not sure of the name) consists of the following:  Air Squat, Situp, Pushup, Pullup, Back extension.  I forget the order, but once one loop is completed, my body is "ready" for the next loop.  That is, starting with squats, then cycling around the exercises, mid-way through my legs are just about completely "rested" for the next round.  I'm at 4x5 rounds now (started at 3x5, which I was unable to complete 2 weeks ago).  

I view Crossfit as a class.  It feels like college-level "training," in that I'm having a teacher show me the movements, and I apply and learn those movements.  I'm currently working on lifting (name escapes me - Burgner?), and those are some straaaaange movements.  I mean, lying in bed last night, I've felt muscles that I've never felt before - quite an odd feeling that I'm starting to get used to the more I attend.

The biggest thing is that my foundational "core" is being built-up.  This is commonly ignored by gym-rats, who go for looks, since the core isn't necessarily that visible.  Working just arms and chest may look good, but in practical use, those people are on the same level as someone like me, who can barely make it through a Crossfit warmup (at least, that's what they tell me!).  My "coach" guarantees that in a month, I'll be much stronger - I mean, I can't even do a pullup at this point, so I've made that as my first Crossfit goal.  Next goal after that?  To complete a Workout of the Day.

blog, complaining, snowboard, crossfit,



The timing of things.  

Something happened quite often during the trip - since nothing was actually "planned" beyond "go to the convention on X days," we ended up wandering around and running across some things that would have otherwise been missed.  My traveling companion and I ended up covering most of downtown.  

For instance, one day we were riding the bus, on our way to an Archie McPhee store, and I wasn't paying attention to the bus stops.  We ended up 2 miles away, and we decided "screw it, let's walk."  We ended up walking through a beautiful park and lake, and after that, discovered a restaraunt with the best food ever made.  A double hamburger and onion rings never tasted so good - we would have never had found it if we got off at the right stop.  Several other occurrences like this happened all during the trip, and my friend and I met quite a few people that we would have otherwise had missed.  The path-not-traveled is more exciting than the main road in this case!

pax09, blog,



Thrings brought home from PAX09:  2 anime figures, 2 "sand" globes, Gunnar glasses, Vibram 5-finger shoes, and a sore throat with cough.  Most of those were free.  Trip was awesome for the most part - my camping gear served me fantastic on the floor of the condo, and walking a minimum of 10 blocks every day plus almost non-stop walking lead to 10 pounds of weight loss.



Pets Gave Me Presents Too...

Within 1 hour of getting home, the cats, in a fit of rage, managed to knock a lamp off (breaking the lightbulb), spill water across half the kitchen, and spray cat-litter over the same half of the kitchen.  The dog was happy to see me, and didn't cause any trouble.

cat, dog, blog, complaining,


Gunnar Glasses

whew...ok, been wearing these Gunnar glasses for roughly 2 hours now.  After the burning sensation, weird color changes, and blurred vision, these glasses aren't so bad.



Computer Specs

Free-play computers...on some nice freaking computers. Razer headphones, mouse, keyboard...Windows Vista 64bit, intel quad-core @ 2.66ghz, 4gb ram.




Just got slapped by Ashly Burch



ooo shiny

Games that caught my attention: scribblenaughts, star trek online, muramasa the demon blade.  Picked up Gunnar optic glasses for 70$, original 140$!!





In PAX line at 6:50am and now in queue room with 10,000 other people.

This is the "business card" that I'll be handing out to random celebrities.




Multitasking is bad, mmk.  Too lazy to click on link: Multitaskers are bad at...multitasking



You just got VERSED.

Ok, about the healthcare reform?  If you're against it, please refer to this:

Matthew 25:41-46 (New International Version)  
Code Sample:
  1. 41"Then he will say to those on his left, 'Depart from me, you who are cursed, into the eternal fire prepared for the devil and his angels.
  2. 42For I was hungry and you gave me nothing to eat, I was thirsty and you gave me nothing to drink,
  3. 43I was a stranger and you did not invite me in, I needed clothes and you did not clothe me, I was sick and in prison and you did not look after me.'  
  4. 44"They also will answer, 'Lord, when did we see you hungry or thirsty or a stranger or needing clothes or sick or in prison, and did not help you?'  
  5. 45"He will reply, 'I tell you the truth, whatever you did not do for one of the least of these, you did not do for me.'  
  6. 46"Then they will go away to eternal punishment, but the righteous to eternal life."

bible, health,


Random Background Content Generator

New background.  It's randomly generated:

Code Sample:
  1. <?php
  2. $db_img = imagecreate(800,600); //size of image
  3. $background = imagecolorallocate($db_img, 0,0,0);
  4. $textcolor = imagecolorallocate($db_img, 150,0,0);
  5. $linecolor = imagecolorallocate($db_img, 0,0,0);
  6. $characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~!@#$%^&*()_+-=`[]{}:;'|,.<>/?";
  7. for($vcntr=-1;$vcntr<60;$vcntr+=2) {
  8.   for($cntr=-1; $cntr<80; $cntr+=2){
  9.    $letter = substr($characters, rand(0,(strlen($characters)-1)),1);
  11.    $spacing = (rand(5,10)+($cntr*10));  //x spacing
  12.    $vspacing = (rand(5,10)+($vcntr*10));  //y spacing
  13.    imagestring ($db_img, 5, $spacing, $vspacing, $letter, $textcolor);
  14.   }
  15. }
  16. imagesetthickness($db_img, 5);
  17. header( "Content-type: image/png");  //specify the type of image
  18. imagepng($db_img);
  19. imagecolordeallocate($linecolor);
  20. imagecolordeallocate($textcolor);
  21. imagecolordeallocate($background);
  22. imagedestroy($db_img);
  23. ?>

Save as something like, background.php.  Next, call it in and it's MAGIC...css:

background: #ffffff url("background.php") fixed;

code, php,




Ok! Test image time...



Nihon-go Nya?


japanese, cat,


Sunday's Kanji

ooo I forgot to post Sunday's kanji.  It's "Day", which, by some encoding miracle, looks like this: æ—¥

japanese, kanji,


encoding issues

aww, the Japanese characters aren't inputting correctly.  Todo: change database encoding.




Technically, "tuesday" in Japanese is 火曜日, but I've shortened it to just the first kanji, which kinda changes the meaning.  It's like, instead of saying "Today is Tuesday.", it would be like saying, "Today is fire."



New Template

So far, I like how this new site template is working out.  I'm going for a minimal-functional approach with the style.  From doing the Flash site, I really liked the look of "quick posts", and in fact, I'm using the same database for these new entries.  It's easier for me to post.  Also, in regarding the flash site...I didn't get far.  That's because I found out that while Flash makes some pretty sites, it isn't fully compatible with everything out there - newer stuff, yes, but older stuff, no - that is, the majority of internet users don't have a way to view flash.  So, I decided to switch back to PHP for this site.  I'm taking a slower, more design-based approach.  I've also had to take the same approach at work - one site I was working on for them, I now have to redo in PHP...while it's "lost work", I did gain valuable knowledge from doing it, which is always a plus.

site, todo,


Sentence Word Pattern

"I do not know where family doctors acquired illegibly perplexing handwriting nevertheless, extraordinary pharmaceutical intellectuality counterbalancing indecipherability, transcendentalizes intercommunications incomprehensibleness".  This is a sentence where the Nth word is N letters long.  e.g. 3rd word is 3 letters long, 8th word is 8 letters long and so on



date test

First official entry, which is a test.