JS Tutorial: Recursive Promise Calls

Leave a comment Standard

The Reason

So in trying to help a co-worker I was looking for an example of how you can do recursive promise calls. To my surprise I didn’t find any soooo now you get this post ūüôā In a typical¬†scenario you would chain your promises one after the other, however in real world situations the solution is not always so cut and paste. For instance you may need¬†to ping a crappy device and wait for the results of the first ping to come back before you can ping the device again. If that’s the case upgrade your device, but if that’s not an option then you can always use this slash and hack solution of recursive promise calls which I would not recommend unless you have to but yeah, real life sucks sometimes. So here you have it, I hope this helps.

Test or fork the working example on Plunker

The Code

//somewhere to store the results of your recursive calls
var results = [];

/**
* Recursive function
*
* @param int val how many times to call this function recursively
* @return null
**/
var promiseMore = function(val) {
  console.log('attempting promise ' + val);

  //defer the promise
  var deferred = Promise.defer();

  //check your recursive conditions, keep looping while true
  if (val > 0) {
    //run your ajax request
    $.ajax({
      method: 'post',
      url: 'https://api.random.org/json-rpc/1/invoke',
      data: JSON.stringify({
        "jsonrpc": "2.0",
        "method": "generateSignedStrings",
        "params": {
          "apiKey": "4e69c4a7-4e47-4811-b110-6c7bfce079db",
          "n": 1,
          "length": 4,
          "characters": "abcdefghijklmnopqrstuvwxyz",
          "replacement": true
        },
        "id": 29970
      }),
      dataType: 'json',
      success: function(response) {
        document.write('Got data for promise ' + val + '<br/>');
        results.push(response.result.random.data[0]);

        //this triggers the recursion
        d = promiseMore(--val);

        //resolve this promise
        return deferred.resolve(d);
      }
    });
  } else {
    doSomethingWithResults();
  }
}

/**
* Whatever you want to do when your recursive promises are done firing
*
* @return null
* @print lots of BS random string results from our promises
**/
var doSomethingWithResults = function() {

  document.write('<br/>============<br/>');
  document.write('Doing something with the results now...');
  document.write('<br/>============<br/><br/>');

  //print out the data we got from the recursive promises
  for (i = 1; i <= results.length; i++) {
    document.write('Results ' + i + ': ' + results[i-1] + '<br/>');
  }
}

//kick off the call to the recrusive promises
promiseMore(10);

 

Html5 Game Tutorial: Pong

Comments 2 Standard

This is a simple HTML5 version of the classic game of pong. Play against the computer for as many levels as you can. The computer gets faster and more competitive the higher the level gets. The game can easily be customized to grow/shrink the paddles and the computer’s difficulty if you’re looking for a challenge after you’ve finished putting the tutorial together. You’ll need a current version of a browser (Chrome, Firefox) that supports HTML5 in order to complete this tutorial. Enjoy!

Download the Code or Fork on Plunker or Try the Working Version

Create the HTML File

The beauty of html5 is that you can program it using javascript. This makes your html file small and super simple. All we need to do is create a canvas for the game and then include the javascript file:

<html>
<canvas id="battlePong"></canvas>
< script type="text/javascript" src="battlePong.js"></script>
</html>

Create the Javascript File

/**
* File: BattlePong.js
* Purpose: HTML5 pong game
* Author: Design1Online.com, LLC <jade@design1online.com>
* Created: 6/7/2016
**/

//global game variables
var game = {
 debug: true,
 canvas: null,
 canvasContext: null,
 frameRate: 1000,
 framesPerSecond: 30,
 fonts: {
 header: '30px Arial',
 regular: '15px Arial'
 },
 colors: {
 net: 'gray',
 background: 'black',
 header: 'red',
 text: 'white'
 },
 screens: {
 winner: false,
 startGame: true,
 levelUp: false,
 continue: false
 },
 ball: {
 x: 50,
 y: 50,
 speedX: 10,
 speedY: 4,
 threshold: 35,
 radius: 10,
 color: 'blue'
 },
 level: 1,
 pointsPerLevel: 3,
 player: {
 score: 0,
 lives: 5,
 paddle: {
 y: 250,
 thickness: 10,
 height: 100,
 color: 'green'
 }
 },
 computer: {
 score: 0,
 paddle: {
 y: 250,
 thickness: 10,
 height: 100,
 speed: 6,
 color: 'red'
 }
 }
};

/**
* Start the game when the window loads
**/
window.onload = function() {

 //init the canvas
 game.canvas = document.getElementById('battlePong');

 //adjust canvas to window size
 game.canvas.width = window.innerWidth - game.computer.paddle.thickness;
 game.canvas.height = window.innerHeight;

 //create the context
 game.canvasContext = game.canvas.getContext('2d');

 //add event listeners
 game.canvas.addEventListener('mousedown', mouseClickHandler);
 game.canvas.addEventListener('mousemove', mouseMoveHandler);

 //start buffering the screen
 setInterval(function() { buffer(); }, game.frameRate / game.framesPerSecond);
};

/**
* Figure out the player's mouse position
**/
function mouseMoveHandler(e) {
 var mousePos = mousePosition(e);
 game.player.paddle.y = mousePos.y - (game.player.paddle.height / 2);
}

/**
* Return the x,y coordinates of the mouse in
* relation to the screen
**/
function mousePosition(e) {
 var rect = game.canvas.getBoundingClientRect();
 var root = document.documentElement;

 return {
 x: e.clientX - rect.left - root.scrollLeft,
 y: e.clientY - rect.top - root.scrollTop
 };
}

/**
* Handle mouse clicks
**/
function mouseClickHandler(e) {

 if (game.debug) {
 console.log('mouseClickHandler()');
 }

 if (game.screens.startGame) {

 if (game.debug) {
 console.log('Removing start screen');
 }

 game.screens.startGame = false;
 }
 else if (game.screens.continue) {

 if (game.debug) {
 console.log('Removing start screen');
 }

 game.screens.continue = false;
 }
 else if (game.screens.levelUp) {

 if (game.debug) {
 console.log('Removing level up screen');
 }

 game.screens.levelUp = false;
 }
 else if (game.screens.winner) {

 resetGame();

 if (game.debug) {
 console.log('Removing winner screen, showing start screen');
 }

 game.player.score = 0;
 game.computer.score = 0;
 game.screens.winner = false;
 game.screens.startGame = true;
 }
}

/**
* Start the game
**/
function startGame() {

 if (game.debug) {
 console.log('Starting game...');
 }

 if (game.player.lives <= 0) {
 game.screens.winner = true;
 }

 game.ball.speedX = -game.ball.speedX;
 game.ball.x = game.canvas.width / 2;
 game.ball.y = game.canvas.height / 2;
}

/**
* Move the ball and the computer mouse paddle
**/
function move() {

 var deltaY = 0;

 //move computer paddle
 var center = game.computer.paddle.y + (game.computer.paddle.height / 2);

 if (center < game.ball.y - game.ball.threshold) {
 game.computer.paddle.y = game.computer.paddle.y + game.computer.paddle.speed;
 } else if (center > game.ball.y + game.ball.threshold) {
 game.computer.paddle.y = game.computer.paddle.y - game.computer.paddle.speed;
 }

 //move ball
 game.ball.x = game.ball.x + game.ball.speedX;
 game.ball.y = game.ball.y + game.ball.speedY;

 //check for paddle collisions
 hitCollision();

 if (game.ball.y < 0) {
 if (game.debug) {
 console.log('Inverting ball Y speed 1');
 }

 game.ball.speedY = -game.ball.speedY;
 }
 if (game.ball.y > game.canvas.height) {

 if (game.debug) {
 console.log('Inverting ball Y speed 2');
 }

 game.ball.speedY = -game.ball.speedY;
 }
}

/**
* Check for ball/paddle collisions
**/
function hitCollision()
{
 //player paddle check
 if (game.ball.x <= 0) {

 if (game.debug) {
 console.log('player paddle collision check');
 console.log('ball', game.ball);
 console.log('player paddle', game.player.paddle);
 console.log(
 'hit or miss',
 game.ball.y > game.player.paddle.y,
 game.ball.y < (game.player.paddle.y + game.player.paddle.height + game.ball.radius)
 );
 }

 if (game.ball.y > (game.player.paddle.y) &&
 game.ball.y < (game.player.paddle.y + game.player.paddle.height + game.ball.radius)
 ) {
 game.ball.speedX = -game.ball.speedX;

 deltaY = game.ball.y - (game.player.paddle.y + game.player.paddle.height / 2);
 game.ball.speedY = deltaY * 0.35;
 } else {
 game.computer.score++;
 game.player.lives--;
 game.screens.continue = true;
 startGame();
 }
 }

 //computer paddle check
 if (game.ball.x >= game.canvas.width) {

 if (game.debug) {
 console.log('player paddle collision check');
 console.log('ball', game.ball);
 console.log('player paddle', game.computer.paddle);
 console.log(
 'hit or miss',
 game.ball.y > game.computer.paddle.y,
 game.ball.y < (game.computer.paddle.y + game.computer.paddle.height + game.ball.height)
 );
 }

 if (game.ball.y > (game.computer.paddle.y) &&
 game.ball.y < (game.computer.paddle.y + game.computer.paddle.height + game.ball.radius)
 ) {
 game.ball.speedX = -game.ball.speedX;

 deltaY = game.ball.y - (game.computer.paddle.y + game.computer.paddle.height / 2);
 game.ball.speedY = deltaY * 0.35;
 } else {
 game.player.score++; //increase score

 //level up
 if (game.player.score > 0 && game.player.score % game.pointsPerLevel === 0) {
 game.player.lives++; //get another life
 game.level++; //level up
 game.computer.paddle.speed += 1.3; //increase computer paddle speed
 game.screens.levelUp = true;
 } else {
 game.screens.continue = true;
 }

 startGame();
 }
 }
}

/**
* Draw the game board
**/
function drawBoard() {

 //draw the net
 for (var i = 0; i < game.canvas.height; i += 10) {
 drawRectangle(game.canvas.width / 2 - 1, i, 2, 1, game.colors.net);
 }

 //draw the score text
 var offset = 100;

 //set the header text
 var header = "BattlePong!";
 game.canvasContext.fillStyle = game.colors.header;
 game.canvasContext.font = game.fonts.header;
 game.canvasContext.fillText(header, offset, 30);

 //set the colors and font style
 game.canvasContext.fillStyle = game.colors.net;
 game.canvasContext.font = game.fonts.regular;

 //set the score text
 var score = "Score: " + game.player.score;
 game.canvasContext.font = game.fonts.text;
 var scoreSize = game.canvasContext.measureText(score);
 game.canvasContext.fillText(score, offset + 5, 50);

 //set the lives text
 var lives = "Lives: " + game.player.lives;
 var livesSize = game.canvasContext.measureText(lives);
 game.canvasContext.fillText(lives, offset + scoreSize.width + 25, 50);

 //set the copyright text
 var copyright = "Games by design1online.com, LLC";
 var copyrightSize = game.canvasContext.measureText(copyright);
 game.canvasContext.fillText(copyright, game.canvas.width - copyrightSize.width - offset/4, game.canvas.height - offset/4);

 //player paddle
 drawRectangle(
 0,
 game.player.paddle.y,
 game.player.paddle.thickness,
 game.player.paddle.height,
 game.player.paddle.color
 );

 //computer paddle
 drawRectangle(
 game.canvas.width - game.computer.paddle.thickness,
 game.computer.paddle.y,
 game.computer.paddle.thickness,
 game.computer.paddle.height,
 game.computer.paddle.color
 );

 //ball
 drawBall(game.ball.x, game.ball.y, game.ball.radius, game.ball.color);
}

/**
* Draw the ball
**/
function drawBall(centerX, centerY, radius, color) {
 game.canvasContext.fillStyle = color;
 game.canvasContext.beginPath();
 game.canvasContext.arc(centerX, centerY, radius, 0, Math.PI * 2, true);
 game.canvasContext.fill();
}

/**
* Draw paddle
**/
function drawRectangle(leftX, topY, width, height, color) {
 game.canvasContext.fillStyle = color;
 game.canvasContext.fillRect(leftX, topY, width, height);
}

/**
* Reset for a new game
**/
function resetGame()
{

 if (game.debug) {
 console.log('Resetting game...');
 }

 game.level = 1;
 game.computer.paddle.speed = 6;
 game.player.score = 0;
 game.computer.score = 0;
 game.player.lives = 5;
}

/**
* Draw the winner screen
**/
function screenWinner() {
 var center = game.canvas.width / 2;
 var height = game.canvas.height / 2;
 var header = "Game Over!";
 var score = "Score: " + game.player.score;

 //set the header text
 game.canvasContext.fillStyle = game.colors.header;
 game.canvasContext.font = game.fonts.header;
 var headerSize = game.canvasContext.measureText(header);
 game.canvasContext.fillText(header, center - headerSize.width/2, height);

 //set the score text
 game.canvasContext.fillStyle = game.colors.text;
 game.canvasContext.font = game.fonts.regular;
 var scoreSize = game.canvasContext.measureText(score);
 game.canvasContext.fillText(score, center - scoreSize.width/2, height + 25);
}

/**
* Draw the continue screen
**/
function screenContinue() {
 var center = game.canvas.width / 2;
 var height = game.canvas.height / 2;

 //set the header text
 var header = "Missed Ball!";
 game.canvasContext.fillStyle = game.colors.header;
 game.canvasContext.font = game.fonts.header;
 var headerSize = game.canvasContext.measureText(header);
 game.canvasContext.fillText(header, center - headerSize.width/2, height);

 //set the info text
 var info = "Click anywhere to start the next ball";
 game.canvasContext.fillStyle = game.colors.text;
 game.canvasContext.font = game.fonts.regular;
 var infoSize = game.canvasContext.measureText(info);
 game.canvasContext.fillText(info, center - infoSize.width/2, height + 25);
}

/**
* Draw the level up screen
**/
function screenLevelUp() {
 var center = game.canvas.width / 2;
 var height = game.canvas.height / 2;

 //set header
 var header = "Level " + game.level;
 game.canvasContext.fillStyle = game.colors.header;
 game.canvasContext.font = game.fonts.header;
 var headerSize = game.canvasContext.measureText(header);
 game.canvasContext.fillText(header, center - headerSize.width/2, height);

 //set the regular text color and font
 game.canvasContext.fillStyle = game.colors.text;
 game.canvasContext.font = game.fonts.regular;

 //set the level text
 var info = "Click anywhere to start the next ball";
 var infoSize = game.canvasContext.measureText(info);
 game.canvasContext.fillText(info, center - infoSize.width/2, height + 25);
}

/**
* Draw the start screen
**/
function screenStartGame() {
 var center = game.canvas.width / 2;
 var height = game.canvas.height / 2;

 //set the header text
 var header = "BattlePong!";
 game.canvasContext.fillStyle = game.colors.header;
 game.canvasContext.font = game.fonts.header;
 var headerSize = game.canvasContext.measureText(header);
 game.canvasContext.fillText(header, center - headerSize.width/2, height);

 //set the info text
 var info = "Click anywhere to start playing";
 game.canvasContext.fillStyle = game.colors.text;
 game.canvasContext.font = game.fonts.regular;
 var infoSize = game.canvasContext.measureText(info);
 game.canvasContext.fillText(info, center - infoSize.width/2, height + 25);
}

/**
* Buffer the screen
**/
function buffer() {

 //black out the screen
 drawRectangle(0, 0, game.canvas.width, game.canvas.height, game.colors.background);

 //draw one if the screens if they're active
 if (game.screens.winner) {
 screenWinner();
 return;
 }
 else if (game.screens.continue) {
 screenContinue();
 return;
 }
 else if (game.screens.levelUp) {
 screenLevelUp();
 return;
 }
 else if (game.screens.startGame) {
 screenStartGame();
 return;
 }

 //playing the game
 move();
 drawBoard();
}

Test Your File

Open your html file in a browser. Make sure that your javascript file is located in the same place. Voila! Now you can tweak and refine it to your hearts content.

HTML5: Analyze or generate your favicon

Leave a comment Standard

RealFaviconGenerator.net analizes your favicon and tells if it is compatible with major platforms. You can also use it to create a favicon if you don’t have one already. It’s a pretty nifty tool. Check it out at¬†RealFaviconGenerator.net

Scrum Mastering

Leave a comment Standard

Note: this is not a comprehensive post, just stuff I picked up during my recent scrum master certification class. Take from it what you will ūüôā

What Is It?

Daily standup meeting, maximum of 15 minutes. 7 people per scrum team is the best setup, 10 is iffy. Open space is best for scrum because it keeps people alert and standing means they’ll be paying more attention. Best to do the standup meeting in an empty room.

Product Backlog

Composed of user personas (ie who will use your software, what kind of person are they) ordered from highest importance to lower importance based on who is the largest user base, how much $$ they have, how frequently do they will use it, what kind of social impact do they have, features specific to them and features shared. This is used to help the product owner determine the business value and priority of each user story. These are then taken to the release backlog. This is who the product is for. In the picture below, features are placed horizontally for the user persona they apply to and turned sideways to indicate they are core functionality. These features are turned into user stories. Typically agile teams will tackle core functionality first since it spans multiple user stories.

product_backlog

Sprint Task Board

Kanban board (Japanese work meaning status, graphic). Has three columns, TODO, IN PROGRESS and DONE. TODO section contains the tasks to be completed also known as Sprint Backlog.

User stories are high level concepts that are broken into tasks which can be completed. User stories should be completed in a single sprint.

Tasks are estimated according to time.

User stories are assigned story points according to complexity of the story.

When you’re planning a story/tasks you plan to keep them busy for 70% of their time since the other 30% might be discovering things that you’ve missed.

Decomposing user stories into tasks is done in the sprint planning meeting.

QA testing should be done once the story is done rather than each individual task.

If you have a separate QA team then you need a QA task for each user story. The QA team would estimate the value.

sprint_taskboard

Sprint Burndown Chart

A chart of the progress done over time. It starts with the total number of estimated minutes and drops as time passes.

A typical sprint has little work and then the work is added and then it falls over time. If that’s happening then you’re not planning your sprints properly and you’re not protecting the sprint (ie adding more work) when you should be. In this case you can see the team has a clear planning issue, therefore the curve of the sprint burndown will be more of an downward trend.

When you start achieving complete sprint burndown you might not even need to use this chart anymore. It’s like training wheels on a bike, once you’ve figured it out you won’t need it anymore.

Release Burndown Chart

This is a similar concept to the sprint burndown except it’s used with sprints instead of with sprint tasks. By calculating the velocity you can add and remove user stories from the release planning in order to get all the story points done in the year.

Definition of Done

The processes that need to be completed for each User Story are put up on a checklist and then each part is checked off as it’s completed. Once all checkboxes are marked the User Story is considered done.

definition_done

Sprint Backlog & User Capacity Chart

Setup a user capacity rundown chart to help organize the team and figure out how much capacity you have for the sprint. The picture below shows the sprint taskboard and the sprint backlog. The white piece of paper is the user capacity chart. User capacity starts with 80 hours for the 2 weeks and then goes down if team members will be out/vacation. Then you add some buffer for other stuff they might end up working on or tasks taking longer than expected. Next you start asking who wants to take which tasks and use up the remaining capacity. This will also help you figure out if you can take on more work or have too much work for the sprint. This is how the team plans to accomplish the sprint goals.

sprint_backlog

Team Calendar

Recommended team calendar setup. Backlog grooming will help you start to plan your user stories for the next sprint. DSM = daily standup meeting.

team_calendar

The Agile Manifesto

Winston Royce invented the waterfall model by documenting what his team was doing during their development process. The core values of the Agile Manifesto are:

Individuals and Interactions – over processes and tools
Working and Software – over comprehensive documentation
Customer Collaboration – over contract negotiation
Responding to Change – over following a plan

The principles behind agile are:

  1. Our highest priority is to satisfy the customer through early and continuous delivery of valuable software
  2. Welcome changing requirements, even late in development. Agile process harnesses changes for the customer’s competitive advantage.
  3. Deliver working software frequently. from a couple of weeks to a couple of months, with a preference to the shorter timescale
  4. Business people and developers must work together daily throughout the project
  5. Build projects around motivated individuals. Give them the environment and support they need, and thrust them to get the job done.
  6. The most efficient and effective method of conveying info to and within a dev team is face to face conversation.
  7. Working software is the primary measure of progress.
  8. Agile processes promote sustainable development. The sponsors, developers and users should be able to maintain a constant pace indefinitely
  9. Continuous attention to technical excellence and good design enhances agility.
  10. Simplicity — the art of maximizing the amount of work not done — is essential.
  11. The best architectures, requirements and designs emerge from self-organizing teams.
  12. At regular intervals, the team reflects on how to become more effective, then tunes and adjusts behavior accordingly

Empirical vs Defined

Defined is something that has architecture and structure, ie a sewing pattern. Empirical is something that isn’t well defined, ie a custom made garment made without a sewing pattern. Software is inherently empirical. The first time you make a feature that you’ve never attempted before you won’t know how long it’s going to take and how much effort it requires therefore you can only provide an estimate. Something that is well defined has been done before therefore you can give a set amount of time and effort to complete the task.

Output vs Outcome

We often focus so much on the output that we forget about the outcome. Producing more code/features doesn’t necessarily mean you’ll have a better outcome. In terms of Agile and Scrum outcome is more important than output.

Product Owners

Agile Is Just That

Since Agile is a set of principals there’s many different ways you can accomplish it. Here’s a look at the Spotify engineering culture which has been using Agile from the beginning. You can quickly see how they’ve deviated from what most people consider the “traditional” ideas.

Scrum Developers

6 people per team, 1 of them is scrum master, is a good idea because it allows them to pair up for pair programming.

Motivating Your Team

Autonomy and mastery and contributing are better motivators than money or bonuses or prizes.

Golden Circle

A. Why you want to create the product (should invoke a vision/feeling)

B. How you’re going to create the product

C. What you’re going to do to create the product backlog

Useful Team Activities

2-Dimensional Axis Chart

X axis is used to denote how much something was successful or unsuccessful (loved it vs hated it). Y axis is used for time. Sticky notes are then added to correspond with the features/activities that occurred. This lets people vote someone anonymously and gives you a feel of what you could work on improving.

Planning Poker

Create cards (100, 60, 20, 8, 1) that represent the story points which are tied to the complexity, effort and cost to create the feature. Assign those values to the user stories via vote (majority wins, hear from outliers and re-vote after). Adding all of those up will give you the total user story points for that release.

Dot Chart

Take the items you can improve from the previous chart and put them on a new graph. Now have people come up and put a dot next to those they feel most passionately about, a larger dot indicates they feel more strongly about it. This will help you identify the areas where the majority agrees there is room for improvement. Sorry it’s blurry.

dot_chart

After a Team commits to sprint goals what authority do they have?

The team does whatever is necessary to achieve the goal.

How are development teams guided during a sprint?

By their collective knowledge and experience.

From Product Vision to Your First Sprint

  1. Create the product backlog from the golden circle design
  2. Create the user personas
  3. Create the user stories (product backlog)
  4. Move the stories into the release backlog
  5. Move the stories into the different sprints
  6. Create tickets for the sprints
  7. Move the user stories from the release backlog into the sprint task board when your release backlog is done being planned.
  8. Now create 7-10 tasks for the user story
  9. Discover any dependencies between the tasks
  10. Now we have to estimate how long it takes to complete the task using 8, 4, 2 and 1 hours (finger voting). Any task that everyone votes for an 8 is too complex and needs to be split into additional tasks. User stories should be completed in under a week, if your user stories are indicating more than 40 hours then you need to split them up.
  11. Once you have the sprints planned it’s time for capacity analysis.
    1. For someone with an 80 hour sprint you should expect that they only have 55 hours of actual work time — this accounts for buffer time.

 

 

 

 

Protect Apache web-server from application DoS attacks (Ubuntu)

Leave a comment Standard

Good stuff.

0xicf

This tutorial assumes that you have a running Ubuntu Server, that networking has been set up, and that you have ssh access.

Apache2 is the default web-server used by many Linux installations. It is not the only one available, or the best for all circumstances, but it covers many usage scenarios. During the installation, you may be asked which web-server to reconfigure automatically. Answer ‚Äėapache2‚Äô.

Install Apache2

Use the following command to install Apache2 and other libraries.

$ sudo apt-get -y install apt-get install apache2 apache2.2-common apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert libapache2-mod-php5 php5 php5-common php5-gd php5-cli php5-cgi libapache2-mod-fcgid apache2-suexec php-pear php-auth php5-mcrypt mcrypt libapache2-mod-suphp libopenssl-ruby libapache2-mod-ruby

Update Timezone and Check Correct Time

To reduce confusion with shared or mirrored data, all servers ought to run as close to as in-sync as possible. Some cryptographic key management systems require accurate time. Lastly, for corporate servers, Sarbanes-Oxley and HIPAA Security Rules require…

View original post 559 more words

PHP Tutorial: Cookies vs Sessions

Leave a comment Standard

If you’re interested in making any kind of web based application then learning how to use sessions and cookies is a must. Many people don’t understand how these work and how they different from each other, and why you would choose to use one versus the other. So¬†put on your thinking caps and let’s get started.

What is a Cookie?

A cookie is a data file that’s written to your browser’s localStorage with data¬†you want to keep track of for a user. So what’s localStorage?¬†The best real world analogy I can give you is to think about it like a 3 ring binder that you can add, update and remove files from. In this example a cookie would be a piece of paper in your 3 ring binder.

How Do Cookies Work?

When you¬†navigate around the Internet your¬†browser is constantly sending and receiving information from the websites you want to access. These are called HTTP headers. HTTP headers contain important information about the requests and responses being sent back and forth from a browser to a server. When someone has an active cookie from your website in their browser’s localStorage it automatically passed in the HTTP headers to the site of origin in the $_COOKIE variable of PHP. Since headers have to be sent before any output setting a cookie¬†must always go at the top of your file.

How do I Create a Cookie?

<?php
$name = "myCookie";
$value = "hello world!";
$expires = time() + 60 * 60 * 24; //1 day
$path = "/";
$domain = "yourwebsite.com";
$secure = 0; //0 for false, 1 for true
$httponly = 0; //0 for false, 1 for true

//let's tell the browser to create our cookie
setcookie($name, $value, $expires, $path, $domain, $secure, $httponly);

//let's make a cookie array too
setcookie("cookieArray[0]", "A");
setcookie("cookieArray[1]", "B");
setcookie("cookieArray[2]", "C");

//you must refresh the page before your new cookies are visible
echo "refresh the page to see your cookies: " . $_COOKIE['myCookie'];

//this will loop through and display your array cookies
foreach ($_COOKIE['cookieArray'] as $key => $value)
{
 echo "<br/>array cookie $key => $value";
}
?>

In this first example we’ve created a cookie called myCookie that stores the value of hello world! In order to see something printed on the screen you must refresh the page. This is because the HTTP headers¬†must be sent from the server to your browser and then from your browser back to the server before they show up in the $_COOKIE variable.

Expires is an optional value that tells the browser how long to keep our cookie before it deletes it. By using the current time() and then adding on an extra 24 hours our cookie will only stick around for a day before the browser deletes it. So in this example if your cookie is created at today at 12noon EST it will expire tomorrow at 12noon EST. When your cookie is expired you’ll have to create a new one if you want to store your $value again.

Next we’ve set a path for our cookie. This is also¬†an optional field and will default to / if you don’t give it a value. This tells the browser where this cookie will be available. So let’s say your website is http://yourwebsite.com and you only want this cookie to be available when you’re inside the http://yourwebsite.com/users/ section or your website. If that were the case we’d change the path to /users/ and then our cookie will only be accessible when we’re in the users directory. When we leave the path at / it means our cookie is available on any part of the domain we’ve given it.

The domain of your cookie is optional. If you don’t provide a domain for your cookie it may default to your current domain name. You can also set your domain name to a subdomain. So for instance, if we had a subdomain of http://users.yourwebsite.com we would set our domain to users.yourwebsite.com then our cookie would only be available when we’re inside our users subdomain.

The secure section of the cookie is optional and should only be set to true if you’re using https.

The httponly is optional and tells newer browser to only make your cookie accessible in the HTTP header. Not all browsers support this functionality, however it has been added to help prevent against cross site scripting (XSS) attacks.

How do I Access Cookie Values?

<?php
//this shows you a single cookie
echo $_COOKIE['myCookie'];

//this shows you all available cookies
print_r($_COOKIE);
?>

Once you have a cookie set you can access it by using the $_COOKIE variable in PHP and then pass it the name of the cookie you want to access or you can use a print_r() call to print all available cookies.

How do I Delete a Cookie?

<?php
//unset the single cookie
unset($_COOKIE['myCookie']);

//unset the array cookies
unset($_COOKIE['cookieArray']);

//update the cookie's expiration date to sometime in the past
setcookie("myCookie", false, time()-1);
setcookie("cookieArray[0]", false, time()-1);
setcookie("cookieArray[1]", false, time()-1);
setcookie("cookieArray[2]", false, time()-1);
?>

Sometimes you want to remove a cookie. There are two different ways to do this. The first is to unset() the cookie and the second is to update the expiration date to a time in the past which will force your browser to remove it. Just like when we set a cookie, you must refresh the page before you will see that your cookie has been removed.

How do I Edit a Cookie?

<?php
//to edit a single cookie value
setcookie("myCookie", "My new value");

//to edit a cookie array
setcookie("cookieArray[0]", "one");
setcookie("cookieArray[1]", "two");
setcookie("cookieArray[2]", "three");
?>

As long as the cookie exists all you need to do is set the cookie with the same name and give it a different value.¬†You won’t see that the values in your cookie have changed until you refresh the page since cookies are sent in an HTTP header.

Cookie Trouble Shooting

  • If you’re having trouble setting a cookie and you’re getting a headers already sent error then you have some kind of output (text, spaces, html, images, etc) that are being displayed to the screen before you’re calling your setcookie() function.
  • Once you’ve added, edited or deleted a cookie you must refresh the page before you can see that your changes have taken place. This is because cookies are sent in HTTP headers.
  • Cookies must be deleted with the exact¬†same parameters as they were set with.
  • Cookie arrays are stored as one file for each index in the array. For this reason large arrays¬†are not recommended. If you need to store lots of data in a cookie it’s more useful to use a single cooke and¬†concatenate the values¬†with implode() then¬†retrieve the data ¬†using explode().
  • If one of the values in your cookie resolves to a false your cookie will be deleted. For this reason you shouldn’t use true/false booleans but instead use 0 and 1.
  • It is not recommended to serialize your cookie values as this can cause security holes.
  • If the user’s browser has cookies disabled or will not allow cookies to be stored then you won’t be able to create a cookie in their localStorage. This problem gave rise to the creation of sessions.

What Is a Session?

Sessions are¬†the best solution for short term storage of data and dealing with user’s browsers that don’t allow the creation of cookies. Sessions will attempt to create a cookie and if the attempt fails will instead propagate via the URL. However a session will only persist over the duration of a user’s visit on your website. Once the user leaves your site the cookie (if one was created) is deleted so there is no persistent information retained about the user in the browser’s localStorage once they’ve left your website.

How do I Create a Session?

<?php
//you have to start the sessions before you can use them
session_start();

//set a single session value
$_SESSION['mySession'] = "hello world!";

//set an array in a session
$_SESSION['myArray'] = new array('A', 'B', 'C');

//set an object in a session
$_SESSION['myObject'] = new myObject();
//this tells php your done making changes to the session
session_write_close();

//session values are immediately available
echo $_SESSION['mySession'];
print_r($_SESSION['myArray']);
print_r($_SESSION['myObject']);
?>

All sessions must be started with the session_start() call unless you have them set to auto start in your php.ini file. Usually this isn’t the case with most hosting providers. In addition session_start can take parameters to configure the length of the session and the storage location, etc, however most hosting providers don’t let you change these settings so I won’t cover them here.

Sessions are sent via HTTP headers just like cookies are (when PHP tries to create a temporary cookie), however since they can persist without cookies the data is immediately accessible once it has been initialized. This means you won’t have to refresh the page to access the data you’ve stored in them.

How do I Access Session Values?

<?php
session_start();

echo $_SESSION['mySession'];
print_r($_SESSION['myArray']);
print_r($_SESSION['myObject']);
?>

Unlike cookies where you have to refresh the page before you can access them, session values are immediately available once they’ve been set. All you have to do is start the sessions (unless sessions have been set to auto start) and then look for the name of the session you want to access.

How do I Delete a Session?

<?php
session_start();

//remove a single session values
unset($_SESSION['mySession']);
unset($_SESSION['myArray']);
unset($_SESSION['myObject']);

//alternative way to remove a session values
$_SESSION['mySession'] = false;
$_SESSION['myArray'] = null;
$_SESSION['myObject'] = '';
//this tells php your done making changes to the session
session_write_close();

//remove all session values
session_destroy();
?>

The best way to remove a session is to unset it, however you can also set the value to falsey or call session_destroy() to remove all session values.

How do I Edit Session Values?

<?php
//you have to start the sessions before you can use them
session_start();

//edit a single session value
$_SESSION['mySession'] = "new value!";

//edit an array in a session
$_SESSION['myArray'] = new array('one', 'two', 'three');

//edit an object in a session
$_SESSION['myObject'] = new myNewObject();

//this tells php your done making changes to the session
session_write_close();

//session value changes are immediately available
echo $_SESSION['mySession'];
print_r($_SESSION['myArray']);
print_r($_SESSION['myObject']);
?>

You can edit session values the same way you set them initially.

Session Troubleshooting

  • If you’re having trouble getting a session started¬†and you’re getting ¬†headers already sent errors then you have some kind of output (text, spaces, html, images, etc) that are being displayed to the screen before you’re calling your start_session() function.
  • Sessions will only last for the duration of a user’s visit or for the session_expiration time that’s set in the php.ini file. You can learn more about session configuration values in the PHP manual.
  • Make sure you’re not doing an unset($_SESSION) as this will make it impossible to register any new values in the $_SESSION.
  • ¬†You can’t use reference variables in sessions.
  • If register_globals are enabled they will overwrite variables with the same $_SESSION name. For instance if you have a session named mySession and register_globals is enabled and you create a $mySession variable it will overwrite the $_SESSION[‘mySession’] value with whatever you’ve assigned to $mySession.
  • If you’re having a problem changing values in your session make sure you’re calling session_write_close() after you’ve made changes to session values. This is especially important if you’re doing a lot of asynchronous ajax calls.

Should I Use a Session or a Cookie?

The best way to answer this question is do you need data on a user to persist once they’ve closed their browser? Sessions will only last for the duration of a user’s visit on your website, they will disappear as soon as the user has closed all windows and tabs that they have open for your website. Cookies, on the other hand,¬†will remain on a user’s computer until the user removes them from their browser or they expire.

Pros & Cons of Sessions vs Cookies

Type Pros Cons
Sessions Excellent for short term data storage

Can access immediately after initialization

Will create a temporary cookie if cookies are enabled/supported, otherwise propagated on the URL

Can be used for authentication

Only last for the duration of the user’s visit

Susceptible to XSS attacks

Cookies Excellent for long term storage of data

Can be used for authentication

Must be enabled/supported on the browser

Must refresh the page before you can access cookie data

Susceptible to XSS attacks

AngularJS Tutorial: Twin List Directive

Leave a comment Standard

Do you have really long select lists that make it hard for users to select multiple options? This twin list directive may solve your problems (well, not the ones in your head hehe) and it’s really easy to use and implement. This requires the underscore library so YAY! no jquery.

View, edit or fork it on Plunker

Create The Index File

So this is pretty simple. All we’re going to do is create a file and give it the ng-app tag with the name of our application then add our new twinList directive. One attribute is for the data they can add to the selection box and the other attribute is for the data they’ve selected. If you want some values selected by default then add them into whatever variable you’re using for your selected data.

<!DOCTYPE html>
<html ng-app="design1online">
 <head>
 <link data-require="bootstrap@*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
 <link data-require="bootstrap@*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" />
 http://code.jquery.com/jquery-2.1.4.min.js
 //maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
 //maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js
 https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js
 //cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js
 <link rel="stylesheet" href="style.css" />
 http://controller.js
 http://twinList.js
 </head>
 <body ng-controller="dataList">
 <section>
 <h1 class="text-center">Twin Lists Angular Directive</h1>
 <twin-list data="listData" selected="selectedData"></twin-list>
 </section>
 <section>
 <h4 class="text-center">Retrieving The Directive Data</h4>
 <p>Use the elements you're passing to the directive attributes:</p>
 <p>Unselected: {{listData}}</p>
 <p>Selected: {{selectedData}}</p>
 </section>
 </body>
</html>

Create The Controller

Now that we have our twin list directive on the page we need to create a controller for our angular app. In in we can pre-populate the values for our listData and our selectedData. These are the default values that will show up in our twin list directive when it loads.

var design1online = angular.module('design1online', []);

design1online.controller('dataList', function($scope) {
 $scope.listData = [];
 $scope.selectedData = [];
 
 var maxOptions = 30;
 var multiplesOfTen = 10;
 
 for (var i = 0; i < maxOptions; i++) {
 $scope.listData.push(i);
 
 //select some stuff
 if (i % multiplesOfTen === 0) {
 $scope.selectedData.push(i);
 }
 }
});

Add The Directive File

Now we add the file for the twin list. If you want the layout file you’ll have to a check the plunker because wordpress is doing some kind of weird regex on the code and won’t display it ūüė¶

The Directive File

design1online.directive('twinList', function() {
 return {
 restrict: 'E',
 scope: {
 data: '=data',
 selected: '=selected'
 },
 link: function (scope, element) {
 scope.data = _.difference(scope.data, scope.selected);
 },
 templateUrl: function() {
 return 'twinList.html';
 },
 controller: function($scope) {
 
 function sort(data) {
 return _.sortBy(data, function(num){ return num; });
 }
 
 $scope.addSelected = function() {
 $scope.selected = sort(_.union($scope.dataSet, $scope.selected));
 $scope.data = sort(_.difference($scope.data, $scope.dataSet));
 }
 
 $scope.removeSelected = function() {
 $scope.data = sort(_.union($scope.selectedSet, $scope.data));
 $scope.selected = sort(_.difference($scope.selected, $scope.selectedSet));
 }
 
 $scope.selectAll = function() {
 $scope.selected = sort(_.union($scope.selected, $scope.data));
 $scope.data = [];
 }
 
 $scope.deselectAll = function() {
 $scope.data = sort(_.union($scope.selected, $scope.data));
 $scope.selected = [];
 }
 },
 }
});