The Continuing Stooooory of a Nerd who Can’t Stop Coding

(That title, just so you know, should absolutely be read in the voice of the voiceover dude from The Muppet Show‘s Veterinarian’s Hospital.)

In the week and a half or so since my last entry, I’ve been working on my registration branch, trying to do All The Things ™. Primarily, I wanted to check the username for uniqueness (well, a close approximation thereof) and I wanted to check the email address against a list of domains known to be used by spammers. The username check went pretty smoothly, all things told. I’m using this:

function checkUniqueUsername($normalizedUsername) {
echo "
Unique Username Check
";
// Get the database instance
$pdo = database::getInstance()->getConnection();
//Construct the SQL statement and prepare it.
$sql = "SELECT COUNT(username) AS num FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);

//Bind the provided username to our prepared statement.
$stmt->bindValue(':username', $normalizedUsername);

//Execute.
$stmt->execute();

//Fetch the row.
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if($row['num'] > 0) {
return false;
} else
echo 'Username is unique.';
return true;
}

Basically, this is a function that is called from the registration page that grabs our database connection info and then asks the database to perform a count of usernames on the users table where the username is the bindValue of :username (meaning $normalizedUsername).

In MySQL, this will return a single-column table, where the column is named “num“. And it will say 0 if there are no entries with that queried username and it will say 1 (… or more?) if 1 (… or more?) exists. Then I’ll return false if there are any entries and return true if there aren’t.

Perfectly logical, perfectly reasonable, works just fine.

So imagine my utter shock and consternation when I tried almost identical code to check the domain of the submitted email address and nothing was flipping showing up in my results. Like, absolutely nothing. Dumping out $row with a var_dump($row) returned a boolean false. What on earth? From my Instagram:

Domain on email is good. But why?
But whyyyyy is the domain gooooood?

You can see in the image that I tested out the query on the MySQL command line and I got 1 row. So why was I getting a boolean false and why wasn’t I getting my 1 count in the row?!

Obviously, the MySQL query, when I typed it out manually on the command line, was fine. And I had logs running, so I could see that the bindValue was working and was fetching the domain in question. So what was the issue?

I took the weekend off because I went to my friend’s wedding in Ontario — I was a bridesmaid — and, rather than sleep like the dead on Sunday night, I coded. I was getting ticked off. Something wasn’t right.

I tried all kinds of things — different bindValue. No bindValue. Different files. Taking the checks out of the functions. Putting them back in. And why on earth was the username check doing just fine but the domain blacklist wasn’t?!?

I finally figured it out on Tuesday evening. I was googling stuff, for the billionth time, and found this issue on GitHub. In it, down a bit, it says:

Running a GRANT command to allow SELECT for this user solved the problem

I froze, the realization crashing over me like a wave. But of course the user has permission, my queries worked… just… fi… I was logged in as root. OH MY GOD, I WAS LOGGED IN AS ROOT.

Fun fact: the user didn’t have permission on the email blacklist table. Just the user table. That’s why everything was empty! That’s why I was getting a boolean false! And I was consistently logging in to the MySQL database as root, which is why those queries worked! I quickly made an edit to my creation SQL file and then manually added permission for my user — voilà. It worked perfectly.

It worked so well that I added the following checks:

  • email blacklist (the original one I’d been having trouble with)
  • disallowed usernames (like admin, etc)
  • voluntary email blacklist (for people who want to opt-out and don’t want to receive invitations to the game)

I still need to compile a list of profane words to prevent people from using them as a username, then add that check.

I also want to add in a unique email check, which will require:

  • storing the email address entered and use this for any communications
  • transforming it all to lowercase for uniqueness, so JULIE @ is the same as julie @
  • stripping periods from the username portion of the email address (julie [at] gmail.com is the same as j.ulie [at] gmail.com is the same as j.u.l.i.e [at] gmail.com) to check for uniqueness
  • stripping any plus signs and anything that follows up through to the @ sign (julie+test [at] gmail.com, for example, is the same as julie+test2 [at] gmail.com) to check for uniqueness

I also want to implement some JavaScript checks, which, shockingly, I actually know how to do:

  • Username between 5 and 16 alphanumeric characters in length
  • Password strength
  • Email address (1) matches email address (2)

Once all the checks are implemented, I want to send a verification email to the email on file, which ideally also holds a “delete me” link that, when clicked, will trigger insertion of that email address into the voluntary blacklist. Of course, if they click the verification link, this will activate the actual account.

Plus, I probably want to ask permission for whether or not I can add them to a mailing list. That seems useful.

So, I’m feeling pretty awesome for realizing it was a permissions issue. I’m feeling slightly less awesome for having made the mistake of testing with root versus testing with the actual user in question, or having forgotten to give the user the right privileges in the first place, but I still fixed it.

And that feels pretty great.

Game update!

I have made a great amount of progress on my game so far, at least as I see it. But before we get to that — and we will! — I wanted to show off my new living room workspace. I’m fortunate to live in a 4 1/2 (two bedroom) apartment by myself, so I have a spare room. The spare room has been the office for just about a year, now, and that’s where I primarily do, you know, work. My previous remote-working experiences have shown me that I’m way too likely to work too hard and too long if the work area is in my living area, so I keep work and life as separate as possible. (Although this past week, I’ve worked exclusively from my living room because I have an air conditioner in here but not in the office. As a result, I’m pretty sure I worked about 42 or 43 hours this week. Oops?)

As for my gaming, my writing, my coding, all of these things have always happened in my living room where my desktop computer is located. Recently, I got rid of the desk I’d had for like almost 20 years, in favour of an identical desk to the one I have in the office. Then, I also got a VIVO dual monitor, desk-mounted stand for my screens in the living room, as well as a boom arm for my Yeti Blue microphone. Oh, and a great little under-the-desk headphone holder, which I’m loving.

Here’s what it all looks like.

Desk, dual monitor stand, boom microphone stand, headphone holder
New living room workspace setup!

And I’m adoring it. Sure, the wires are absolutely revolting, but I’m not going to cable manage anything just yet, because I’m on the cusp of getting a new computer. I’m going to be attempting to assemble it myself from the various component parts. I’m not much of a hardware person (although I’ve installed RAM, a secondary HD, a primary HD and a video card), so my friend, Andrew, has sworn to me that he’ll FaceTime me and walk me through the assembly if I need a hand. So with a new computer imminent, cable-managing right now is dumb. Once that’s all set up, though, you can bet your bippy I’ll be doing my best to hide all those unsightly cables.

So the new workspace is pretty great for the time being and having the monitors mounted to the stand is AMAZING. I’ve reclaimed so much desk space I hadn’t even realized I was missing. I genuinely like sitting here and my HUE lights just make me happy. 🙂

Okay, so that’s the update on the workspace in my living room. Now, on to the game stuff!

Once upon a time, I used to chat on IRC (Internet Relay Chat) pretty consistently. Like, I’d go to bed for the night, but just leave my computer and IRC client running, and I would always have my IRC channels in the background while I was working. I mean, I was working as an independent contractor while I was the Chatting Online Guide at About.com, sooooo I’d have to say that was pretty reasonable. Anyway, I made friends online, as one does, and several of them were in Australia. Australia has a pretty significant time difference between the East Coast of North America and, well, virtually anywhere in Australia, so getting to chat with the Aussies usually meant someone was up late or up early.

One day, I woke up and stumbled to my desk and blearily tabbed over to mIRC and, in one of my channels, one of the Aussies had been freaking out several hours prior and had eventually gone all-caps in her exclamations and said “WAKE UP I NEED TO ENTHUSE!!!!!!!”

I have had this deep need to enthuse about my game for a couple of days now, and I don’t really have a lot of people in my life who, a) care about this little project of mine, and b) know anything about programming. So that’s why I’m enthusing here, I guess. 😉

Updating my personal site last week is what got me itching to code this week, because I’d played around with self-submitting forms, which is currently how I’m testing out question/answer functionality. So I sat down to try to finish getting PDO to work for me on my test page.

I’d also made a change to my docker environment in that I created three database users that are limited in their permissions — one can edit the users table, one can edit the tokens table and one can read the questions table. So that involved making sure I was using the limited questions user rather than the root user, too.

For some reason, my MySQL PDO command wasn’t asking one question in isolation, which pointed to an issue with my select statement, which, in turn, pointed to an issue with how I was “randomly” selecting a question row. Once that was sorted, my question was being asked! Hooray! Being able to pull in that question from the array felt pretty great. Of course, in order to get to the point where I was able to replicate what I’d done when I was messing around back in January, I was going to need to be able to check the answer and inform the user if they were right or not.

So my first major issue was “how to carry over the user’s answer to the page upon submission?”, but it ended up not being that hard because I remembered I could just grab the $_POST[‘playeranswer’] data and stash that in a variable. I also strtolower() it, because all the database answers are in lowercase, so for a match, the cases need to match, too.

That worked, but if I wanted to also display the last question, along with its correct answer, and the user’s own answer (plus the correct/incorrect validation), I was going to need to pass along the row from the last question. So I pass that along, currently, in a hidden form field so I can snag that from the $_POST[‘lastquestionrow’] field.

When you load the page on a GET, you don’t have any of that, but when you load it on a POST, you now get last question row and player answer stored into their proper variables. Then, the database is called and we start getting into the fun stuff:

  • hey, here was the row for the last question, please display for me the question and the answer
  • also here’s what the player said, so show that
  • if they match, say so and grant the player a point, announce new score
  • if they don’t match, say so, don’t grant a point, repeat score
  • if they don’t match but player has no score, say so, don’t grant a point and let the player know they currently have no score

Now, for the most part, this worked just fine. However, I realized, after having done this, why my teachers in my certificate always recommended I go for a does not equal comparison rather than a strict equality comparison. It’s because a strict equality comparison is messed up, yo. I noticed, through my testing, that I was occasionally getting false positives while going through answers that had more than one possible answer and if one of the possible answers has more than one word. So like, what’s the capital of Canada was fine, because that’s Ottawa. But for asking about the capital of the province of Quebec, that’s where issues cropped up. Your choices are “quebec” or “quebec city”. And yet, somehow, with my earlier logic, “city” was acceptable.

In another example, “flash” was an acceptable answer instead of “barry allen” or “the flash”.

And, in the weirdest example, the name “david” was accepted when neither answer was even remotely close to “david”.

So there was something wrong with my comparison logic. At first, I thought it was just that I was using == instead of ===, so I thought okay, stick in a third equals sign there and I’m good! That’s what makes it a strict comparison, right? Right. Should be sorted!

Alas, no, the issues continued, but I went to bed.

The next day, the strange bugginess was in the back of my head all day long. When I sat down to code a bit after work, I really could have rewritten the logic to test for a failed match rather than a positive match, but I was curious as to what in tarnation was going on. How is “david” considered equivalent to one of two completely unrelated names? (Adam and Carter, if you’re curious.)

I sat down and did more robust testing, writing down all the weird results. So I pulled out my Programming PHP O’Reilly book and looked up comparisons, because everything I’d googled was telling me that === should do the trick.

And this is where I learned about the string compare function: strcmp()

The way I understand it is that strcmp() will compare two strings and will return a 0 if they’re equal. So, this then meant I needed to tweak my logic again. A simple “if $string1 === ($string2 || $altString2)” type of statement wasn’t going to work. I needed to funnel the player’s last answer into a use of strcmp() and compare it with the last answer as well as the last alt answer, if the alt answer existed. So here’s what I ended up with.

$mainVerificationCheck = strcmp($lastPlayerAnswer, $lastAnswer);
if ($altAnswerExists == true) {
$altVerificationCheck = strcmp($lastPlayerAnswer, $lastAltAnswer);
}
if(($mainVerificationCheck === 0) || ($altVerificationCheck === 0))

Basically, that says, here’s the mainVerificationCheck variable, which is comprised of a value returned by using the string compare function. By default, this compares the variable containing the last player answer as well as the last correct answer from the last question. Additionally, if the last question had an alternate answer (like in our Quebec City/Quebec question), create a variable called altVerificationCheck and use the string compare to test the last player answer against the last alternate answer.

Then, if the main check returns a 0 (which means a match) or if the alt check returns a 0, then do stuff. (In this case, it’s notify the user they’re right and their points have increased.)

I’ve been playing around with Instagram stories and captured my bug hunting and fix. Here are the pics from that.

Funky bugs in my game.
Bug hunting!

 

=== or strcmp()?
strcmp()? What is this?

 

Fixed my comparison issue
strcmp() for the win!

 

So yeah, I got a lot done over the last couple of days. I worked on stuff on Thursday night and then Friday night well into Saturday morning. And I was going to start in on registration and user creation tonight — my Docker containers are up and running, I have a fresh branch of master that I’ve checked out to a new branch called registration, but I’ve had a headache pretty much all day Saturday and it’s like, four in the morning on Sunday, so I should probably go to bed. I’d have gone to bed way earlier but my headaches are largely sinus-based, so lying down is actually super painful when my sinus cavities are full. Gravity helps the head to drain, I guess!

So my headache meds seem to be kicking in and I probably won’t be able to get back to coding until at least the week of the 6th, because a friend of mine’s getting married on the 4th, and I’m a bridesmaid, so I have a lot of stuff to do this week, then I go to Ontario for the wedding on the weekend. Looking forward to finalizing my computer build either this week or next. I think the new computer will encourage me to do even more coding, and possibly not at ridiculously late hours of the night!

When I started talking about this project, back in January, I was thinking an alpha version might be available in last September.

… yeah, not sure that’s going to happen, hahaha. The base functionality of questions and answers? Sure. But not the game itself.

Anyway, the next task is going to be user registration and login, so I can start saving stuff to the user table and saving stuff to sessions. I want to not have repeat questions, so I’m going to save a certain number of questions in the session (I think?) and check that when doing the random question picker. I think.

Maybe. We’ll see how this goes. I’ll definitely keep you, whoever you are, dear reader, posted on my progress.

 

The Great SSL Weekend Extravaganza

This past weekend, I embarked upon a task of positively Herculean proportions. With Chrome 68 coming out and the continued emphasis by Google to ensure HTTPS is the default on sites, rather than HTTP, I decided to go ahead and add SSL certificates from Let’s Encrypt to, oh, 13 domains that I host, both for myself and for friends and family. (I had a lot of fun posting to my Instagram story about it, too!)

I’d done a test run with one of my domains earlier in the year and, though it wasn’t without a challenge or two, it seemed to go just fine.

The trouble, of course, came when dealing with various WordPress sites (including this one!) because how do you change ALL THE LINKS in ALL THE POSTS and ALL THE PAGES to HTTPS from HTTP? Well, thankfully, there are database queries and free plugins.

While I’ve generated and installed the certificates for all thirteen domains, one of them basically has no traffic, so I haven’t updated that WordPress database yet. Then, there’s another one that is just too daunting for me to bother with right now — two WordPress installations, plus my own hand-coding elsewhere, plus redirects galore… So I said to hell with it for now. I haven’t forced SSL on for either, but both are accessible via HTTPS. When I have more time, I’ll go through them both and force SSL on, after dealing with updating the references in the databases.

In addition to that, I redid my personal homepage at juliemartin.org. Not only does it also have SSL enabled (and forced), but it’s also mobile-responsive! It’s not as pretty as I’d like it, but it’s not bad for a quick redo. I also used PHP Mailer to create a contact form. Additionally, I put in a hidden field that humans can’t see (owing to CSS) as a honeypot for spammers and if that field is filled out, then the form fails to send.

It was in the creation of this form that I realized how much the form’s enctype matters, because if you choose the wrong one, it’s entirely possible that your $_POST array will be completely empty. It worked fine on my own local environment, but I suspect it was getting wiped out by my host’s mod_security in Apache, because I did get a couple of 406 errors, though they weren’t consistent. I spent a good amount of time on Saturday evening wondering why the hell my $_POST array was empty, checking everything, from making sure the form was set to POST to double, triple, quadruple checking the field names.

In the end, the enctype was the issue and everything ran beautifully once I fixed that up. I used Postman to test out a variety of differences between my browser’s POST request and finally found it.

Then I was able to easily code in the hidden field and the failure if there’s anything in it. (Fun fact: I’m pretty certain that the placeholder text in a form does, indeed, count when your conditional is if (!empty ($_POST[’emailaddress’]) though it would most certainly require more testing.)

Overall, I did a crapton of stuff this weekend, I learned a ton of stuff and my brain hurts sufficiently.

In other news, making moves towards ordering parts for a new computer. I’m getting pretty excited about it, not gonna lie.

Next post, pics of my living room setup, I promise. I just got a desk-mounted dual monitor stand from Amazon, which I am in love with and really makes my workspace look awesome.

Until next time!

PDO, why must you hate me so?

My glorious four-day weekend is just about over and I did a bunch of coding and no writing. I did, however, sit happily in air-conditioned bliss. When the humidity makes it feel like 46C (114F), you kind of don’t want to move, much less leave an air-conditioned room.

I made a lot of progress on switching things over to PDO from mysqli, but it’s still not done. I’m still flailing around, trying to figure out wtf the problems are that I’m experiencing. Here are some of the issues I’ve had so far this weekend, all of which are resolved:

  • I had an errant space in <?php. And that borked everything that came after it. Why I didn’t see the space between the question mark and the p is unknown.
  • I couldn’t figure out how to copy a file to the directory above (and thus, outside) the web directory in my PHP Docker container.
  • I further couldn’t figure out how to actually edit a file in that directory once I was able to copy it, but then inserted a nano install in my Dockerfile. (My original linux/unix experiences were heavy on pine, tin and pico.)
  • I decided to actually create and use limited-access users for the database calls (as one should) and then had a hell of a time ensuring they could connect to the MySQL Docker container.
  • Even with limited-access users being valid users, I couldn’t run a simple select statement.

All that’s been taken care of. Now it’s a matter of actually writing the code to get a question pulled from the database, displayed to the user and then matching their typed answer with the answer in the database. I gave it a couple of half-hearted attempts tonight, once I finally got one of my database users to properly make a select statement, but figured I’d accomplished enough.

A lot of it this weekend was making sure things were being done right. It’s hilarious how I’m still not to the point where I was when I was writing things with mysqli. Just need more time. This is not going to be simple and it’s not going to be solved quickly.

Speaking of “quickly”, the process of starting up my Docker containers is not so much with the speed. I obviously desperately need to update my computer, because there’s not a lot going on in that startup process, but it takes — and I timed it — upwards of eight minutes to get both the PHP and MySQL containers running. Now, this didn’t pose a huge problem in PHP II, because the containers were configured by our teacher, so we literally did docker-compose build and docker-compose up to start up and then docker-compose down when we were done. So it’s one thing to spend eight minutes or thereabouts waiting for your containers to come up when you do it once, but a whole other thing to do it numerous times in a day. Come to think of it, there’s probably a way to refresh the configuration/etc without killing the containers and starting anew, but that bears more investigation.

And speaking of refreshing configurations and such, I spent a couple of hours updating my IRC server, services and proxy monitor on Sunday. I’m a terrible person because I don’t update often and, when I do, it’s usually because a new major release has come out. As such, I generally have to start from scratch because various updates are incompatible with my existing  configuration file. But not this time! So I backed everything up (like, twice) and then proceeded to install over my IRC server with the new version and then restarted the server and that worked fine. Same with services. Same with the proxy monitor.

I’d set aside three hours for it and it barely took two — and that included all the backing up and reading I was doing to ensure I knew what the hell I was doing. It went so smoothly! I fully expect the earth to open up and swallow it, now.

And on that note, I should go to bed. Back to work tomorrow and I’ll be working from my air-conditioned living room instead of my non-air-conditioned office.

I’ll post some pics of my living room desk with and without my work laptop next time! I’m really loving how it’s turned out.

Back to it?

Tonight, I spent a couple of hours playing with code for my game.

Not that I got particularly much done, mind you. Most of the time was spent flailing wildly on the command line, trying to remember the magical combination of words and characters to push something to GitHub, or remember how to connect to my Docker containers.

I’m trying to switch from using MySQLi to PDO for the database requests, as I’d mentioned back here. It’s the most robust method, especially when you use prepared statements. And, for my game, the majority of database calls will be to a question/answer database, so prepared statements make total sense because they’ll be reused a ton.

Programming is really a skill you need to practice to retain, just like any other language. It’s been about three months since I’ve done any kind of programming at all and more than that since I’ve touched PHP. So I am definitely out of practice and need to spend time working on that some more. So the PDO thing was what I wanted to tackle tonight. It didn’t happen. But I feel better for having tried out a few different options and learning from those attempts.

This weekend is a four-day weekend for me, thanks to a company-wide holiday on Friday and observing Canada Day on Monday. My goals include a lot of sleeping, some writing, some coding and a lot of sitting with my air conditioner on high. It’s supposed to be more than 40C (104F) with the humidity, so that’s disgusting. I love my city, but living in a place where it gets down to -40C and gets up to +40C is kind of stupid.

Anyway, it’s late, so I should get to bed. More updates as events warrant!

Updates and such

First, the last two months of my life have been kind of hectic. I finished my courses. I went to Las Vegas. I went to Lake Tahoe. I came home and wanted to sleep forever… 🙂 I had a birthday. All told, a whirlwind.

Things are calmer now, so I decided to buy a new desk. No, not for my home office, but for my living room. I’ve had this monstrous L-shaped desk with shelving and storage on the side of it for approximately forever. Like, 1999. It’s served me well. I remember putting it together in my bedroom in my parents’ house.

It came with me to my first apartment in 2001.

And my second apartment in 2002.

And, uh, I’m still here.

Basically, the desk hasn’t been touched (moved, emptied, etc) in 16 years. So I’ve spent the last two weekends emptying it and cleaning it, because I’m getting a new desk from IKEA (yes, the same Linnmon/Alex desk as in my office) and getting rid of this one.

Anyway, I’ve been deeply nostalgic as I go through everything from old cable and phone bills to old birthday cards from my grandmothers (both of whom have passed), from old photos to old tickets to the third Harry Potter, which I saw in freaking Newcastle, England.

And now everything is either thrown out (there was a lot) or stored (also a lot) or temporarily stored because I still need those items regularly.

Which leaves me with the desk.

Obviously, I have to move the computer stuff and lamp, which I’ll do on Wednesday, before dissembling the desk into its two major halves. It gets picked up on Thursday evening and the new desk arrives Friday afternoon. So next weekend will be me building another IKEA Linnmon/Alex desk.

I’ll miss the desk. I will. I’ve had so many memories at this desk, strange as it may seem. I’ve blackjacked people over the head in Thief, I’ve conquered worlds in Civilization V, I’ve written hundreds of thousands of words here. I’ve defeated innumerable dungeons and raids and bosses in World of Warcraft, I’ve spent hours laughing as I recorded podcasts, even more hours editing those podcasts… I have spent a lot of time at this desk over the years.

And I know, it’s just a desk. It’s a piece of functional furniture that I picked out and bought and assembled almost two decades ago.

But the laughter and tears (don’t underestimate the emotions one can feel when writing or interacting with others online), the joy and sadness, the victories and defeats that have happened while seated at this desk are uncountably immense.

While I was going through All The Things, I found a ton of old CDs with lots of data on them, too. Old versions of this very website existed on these CDs, for example.

Also, old stories. Old photos. Even old videos. One day, I’ll try to pull some out and do something with them or whatever.

For now, I’ll sit here, lost in thought, overcome with memories and feelings from the last 19 years of my life.

That’s All, Folks!

It’s nearly 4am on Thursday, March 22nd, 2018.

I should be in bed.

But I just handed in my final JavaScript assignment and my brain is still whirring. Lots of things on my mind tonight-slash-this-morning, so I definitely need to let the brain settle a bit before I try to sleep for maybe five hours before I then work for 6-6.5 hours, before I then go to class.

It was sometime in, oh, June or July of 2016 that I said to myself, “Self, don’t you want to be more technical? Don’t you want to learn PHP?”

My job, at the time, was Head of Customer Success for a small Montreal-based startup and I was forever not quite understanding everything. Tokens? Authentication? PHP errors? Yeah, so it would have been useful, I thought, to learn PHP.

Surprisingly, I don’t learn amazingly well through online classes. Maybe I’m old-school — or just old! — but I need to be sitting in a classroom, listening to a teacher. I know, I know, silly. But still, it’s how I learn best. So I’d looked up what I needed to do at my old university’s Continuing Education department to get to PHP. Pre-requisites: Java Programming, SQL and HTML5/CSS3.

… yeah, so I said screw that. But by fall I’d been thinking about it more and more. I thought, at the minimum, maybe I’d just take the SQL class, just 20 hours or so of the course, one evening a week for a couple of hours, for ten weeks. So I signed up for the fall semester.

And then I saw that the HTML class, taught by my favourite teacher in ContEd, was available, so I swapped SQL for the winter semester, figuring I’d take HTML and update my skills. You know, learn about things like media queries and responsive webpages and maybe not use tables for layouts when I could use divs. It would be a good test for me to see if I could handle four hours of class once a week for ten weeks while juggling work, since it would be mostly a review for me.

Well, I got like 4-5 weeks into the class and then I got laid off at work. So I kept going with the classes. HTML and SQL were already paid for. So I kicked ass in HTML and did very well in SQL and then I took Java, which was so very, very painful, then PHP (with virtually no break in between), oh, and I started a new job in there. And then, finally, I had a bit of a break between PHP and PHP II.

Finished PHP II (it was an awesome class) and that left me with one class left to get a diploma: JavaScript & AJAX. So I took it this winter.

Really not a fan. hahaha.

I’m sleep-deprived and have an exam in, oh, under 14 hours, so that’ll be fun. But this is it. Unless my assignment is a failure (it isn’t), or unless I completely bomb the exam (I’m really hoping I won’t), I should be finished with JavaScript & AJAX after I write my exam tonight.

And with it, I’ll be done with my certificate in Front-End Web Development.

And with that, I’ll be done with my Diploma in Web Programming.

18 months after I started, I have learned an insane amount of stuff. A short list:

  • HTML5, CSS3 and media queries
  • SQL: creation of a database and database table, select statements, nested queries, all from the command line!
  • Java: my real intro to programming, with variables, conditional statements, for loops, data types (booleans, integers, floats, etc), use of NetBeans (ugh), keyboard buffers, regex, arrays, data validation, using database queries to pull an item from a database, object-oriented programming and the like.
  • PHP I: Basically, all the Java stuff but done in PHP, including some cool server stuff. Plus more MySQL.
  • PHP II: Man, where to even start? Docker and virtual containers, GitHub and the command-line interface, repos, pulling, pushing, fetching, etc, APIs, security, passwords and hashing, the cloud, indexing and search within MySQL, unit tests, stuff about composer… Honestly, I probably learned the most in this class and I loved it.
  • JavaScript & AJAX: Well, I can validate forms. haha. And make AJAX calls! And write stuff that’s very similar to Java and PHP within JavaScript. I had no idea it was so deep and complex and we didn’t even touch on frameworks.

I didn’t learn a ton of new stuff here, but it’s a 300-level class and PHP I is 400 and PHP II is 500, so I basically knew most of the stuff. But it’s always good to have it refreshed.

So tonight, after my exam, I’ll have completed 260 hours of class over six courses, in 18 months. I didn’t take any breaks, I just took one every semester until I was done.

Were I to do it over again, I’d book more time off of work for final projects/exam prep. Alas.

I plan to spend several months catching up on everything from sleep to TV shows, plus doing a lot of writing and some coding on the side. There is my game to write, after all!

I have deeper thoughts about this experience. I have more to say about how I feel after all of this.

But it’s 4:28am and I should wake up in about five hours. Maybe I’ll be even more loquacious next time.

More complexities

Due to the fact I had a JavaScript midterm last week, I haven’t done any coding for my game project. That’s okay. This is not going to be something I’m going to hack out over the course of a week. Or a month. This is going to take a long, long time.

In the meanwhile, I’ve been doing some reading.

Let’s talk about usernames: Excellent read. Also, horrifying. As I was reading, I came across this line:

There’s our user table, there’s our unique username column. Easy!

That’s exactly what I was thinking. That’s how to make something unique, right?

And then I kept reading. Case-sensitivity, normalization, punycode, other alphabets… Also, single-script confusables (ie: a lower-case L and a number one and a capital i, l 1 I, all look ridiculously similar in many fonts. Is that a problem? And what about unique emails? Gonna have to take the + sign that Gmail allows and nuke everything after it to ensure true unique mails. Also, remove all periods in the email’s username portion. And what about names like “root” or “admin”? Probably should disallow those, too.

So that alone gets complicated.

Then I did some more reading about PHP web apps in particular, in terms of best practices, just to make sure I’m on the right page. This page, PHP Best Practices, is a great resource. It’s mostly in English, basically, which is useful. Or, at the very least, I can understand it. It also goes into detail about why or why you should not use a specific way of doing things.

So in terms of storing passwords, I’d been right — this resource recommends bcrypt hashing. That’s wonderful.

In terms of connecting to, and querying, a MySQL database, not do much. Turns out the method I hated most (PDO – PHP Data Objects) when learning is the newest and most robust method, particularly when using prepared statements. All of my testing code to date uses mysqli, so that’s something I need to rewrite to ensure it works okay.

So lots of reading and understanding done there and more to come, I’m sure.

In other news, I successfully installed a Let’s Encrypt SSL certificate all by myself for one of my (many) domains. The only bummer is that they expire every three months. Still, refreshing them takes like, five minutes. So it’s not too bad. And it’s free! That’s one of my projects for this spring: Get all of my sites to be on SSL, using HTTPS, even though I basically don’t ask for any user information whatsoever. Why? Because Chrome is starting to list things as “insecure”. So that’s something to do.

Finally, in terms of work, can I just say how delightful it is to have a job that allows us to work flexibly? I was about an hour and change short of my 40 hours last week, so I made up the time this weekend. Of course, in making up the time this weekend, I actually worked for almost three hours. So because I worked an extra two hours or so, those are banked against next week’s time. Normally, I work 9-hour days on Mondays or Tuesdays (sometimes both) in order to make up for class on Thursday, but seems as though I won’t need to do so this week. And the bonus is, I’m all caught up on my own outstanding tickets. Whew.

All right, it’s now past midnight, so I should consider going to bed, now. Have an excellent week!

Continuing Adventures in Coding

Well, it’s 12:43am on Wednesday, February 14 (… happy Valentine’s Day?) and I feel like a coding badass.

Why? Well, two reasons.

The first is that I was working on Tuesday and someone had a ticket open with me that was complaining that one of our plugins was forcing use of the http protocol, rather than the https protocol. And I’m like “pfft, no way,” so he sends me a video and shows me and I’m like… “whaaaat?”

So I went digging around on GitHub in the code.

Sure enough, in the abstract class file (which, in case you’re unaware, serves as a template, if you will), there was a reference to a variable for a base URL… which had http hardcoded as the protocol to use.

Now, that might not seem like a big deal, because so much of the web is insecure and has no real need to be secure. But we deal with ecommerce sites, so HTTPS is, more often than not, in use on these sites. So why on earth didn’t the developers use // as a protocol agnostic prefix to the base URL? No idea. Literally, no idea.

Of course, this was coming from me, with my whole entire 80 hours of PHP under my belt, so although that’s what it looked like to me, I wasn’t certain. I flagged it to a developer who took a look and said “great sleuthing!” and she pushed some changes which made it into the release that’s going out this week. I was so surprised that:

a) I was right

b) This code existed in the first place!

I mean, I haven’t done any secure sites ever, but I learned at my last workplace that one needs to account for whether or not the client will be using an SSL certificate, so you should always use // instead of specifying http or https. Makes sense. I checked the history and it appears that the two lines with http in them had been there since, oh, the start of the plugin. hahaha.

So, that’s one reason I feel like a coding badass.

The other is that, with a nudge from an online acquaintance, I managed to finish my JavaScript assignment in which I have to show the current time in six separate timezones: Houston, London, New York, Seattle, Sydney and Tokyo.

It took me longer than I’d like to admit, but I finally got it to work. The main issue was that I’d accidentally written newTime=newtime.settime(newvalue) instead of just newtime.settime(newvalue).  ¯\_(ツ)_/¯ The secondary issue was that my universaltime variable wasn’t going to GMT/UTC, for some reason, which ended up being “Julie, you’re stupid and altering the wrong variable before passing it back.”

Anyhow, my JavaScript assignment is now done and tomorrow night, I can actually study for my midterm on Thursday.

Just six more classes (including Thursday) before I’m done! And then maybe I can return my attention to my game.

No real updates there, although I now have figured out I’m probably just going to end up using bcrypt as my password hashing method. Literally, the only piece of personally-identifiable information I think I want to store that belongs to a user is an email address. No reason for anything else, so I don’t think I need to go all out for security. Still, I did a lot of research and reading and feel a lot more comfortable with what I’m going to be attempting here, at least when it comes to users.

Okay, it’s getting late and I should be up in about 8 hours to work, for eight hours, and then study for the rest of the evening.

Oh, the complexities!

It occurred to me today that if I’m going to build a game that I expect other people to play, that they’re going to have to log in to… I’m going to need an SSL certificate.

That’s down the road, of course, but it would be foolish to have any data transferring between individuals and my site without using the HTTPS protocol.

That’s not the only complexity. I was thinking about how to best go about a registration/login process and it dawned on me — I need to figure out password hashing, salting and that kind of stuff. I already know stuff like “md5 is bad” and we used bcrypt in my PHP II class, in conjunction with the password_hash function which (when using PHP 7, which I am) adds a salt. But is that going to be enough? My reading suggests yes, but it’s still not fully solidified in my head, so more reading is required.

Additionally, password resets! My reading recommends a one-time, short-expiry token to allow people to log in from the email sent out to them. While I think I know how to pull that off, thanks to PHP II, I suspect this is going to be a pain. Still, I want to make certain that people’s accounts aren’t easily compromised, so I’m inclined to spend more time than less when it comes to this kind of thing.  And I’ll need more time because all of this is also still fuzzy in my head.

Still, in order to make any kind of forward progress in terms of gameplay, I need to make sure I have a login functioning properly, even if I don’t do a registration yet and just populate the user table with a couple of user accounts when I spin up my Docker containers. That means I definitely need to get the password hashing stuff figured out and understood properly before I implement something. Then, maybe I can move forward in the rest of things.

I knew this was going to be a hell of a big project to undertake. I may have underestimated it a little bit. Still, all of this learning is pretty great. More of it to come, clearly.