blog and things oh hi there!
Passing cookies between requests in Guzzle

I recently had to write for two endpoint calls that shared a cookie to indicate that the request was authenticated and here is how I did it with guzzle.

Get guzzle via Composer :

{
    "require": {
        "guzzlehttp/guzzle": "^6.2"
    }
}

And here is an example of how you might use it between two requests.

require __DIR__.'/vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;

try {
    $guzzle = new Client();
    $cookieJar = new CookieJar();

    $response = $guzzle->post(
        'https://first.request', [
            'cookies' => $cookieJar,
        ]
    );

    $response = $guzzle->get(
        'https://next.request', [
            'cookies' => $cookieJar,
        ]
    );

} catch (Exception $e) {
    throw new Exception($e->getMessage());
}

As you can see, the only trick to it is to share the same cookie object between the two requests. That's it!

Format Sass with Sublime Text 3

I am a huge fan of auto formatting code whenever possible and here is how I got it to work with sass-convert and Sublime Text 3.

Start by installing sass-convert globally:

npm -g i sass-convert

Once installed, hook it up with Sublime Text 3 via a custom build system. In Sublime Text 3, goto Tools > Build System > New Build System.... Name the file to scss-sublime-build and here is what goes inside.

{
    "path": "/Users/kjung/.rvm/gems/ruby-2.1.2/bin",
    "selector": "source.css, source.sass, source.scss",
    "shell_cmd": "sass-convert -i --indent 4 --unix-newlines \"$file\""
}

As you can see, I simply define the path for the sass-convert binary, which you can get by running which sass-convert in your terminal. The selector tells sublime which filetypes this build system applies to. Lastly, the shell_cmd is the actual command that gets triggered and $file is a variable that holds the absolute path for the current file you're working in within sublime. Look at the manual for sass-convert and add in any options you want in the shell_cmd to make it work the way you want.

Make sure for Tools > Build System, you have Automatic selected and try to build a scss, sass, and css file by hitting the Ctrl + B hotkey.

Hopefully you'll see a flicker and your code formatted!

Adding torrents to ruTorrent with PHP

While using ruTorrent, I was looking for a way to add torrents through PHP directly to the ruTorrent WebUI. Here is a simple way to do this using guzzle.

First install guzzle through Composer. This is what goes in composer.json:

{
    "require": {
        "guzzlehttp/guzzle": "^6.2"
    }
}

And here is the guzzle request that posts the torrent file.

require __DIR__.'/vendor/autoload.php';
use GuzzleHttp\Client;

try {
    $guzzle = new Client;
    $response = $guzzle->post(
        'https://server.address/rutorrent/php/addtorrent.php', [
        // if using basic auth, place credentials here
        'auth'      => ['username', 'password'],
        'multipart' => [[
                'name'     => 'torrent_file',
                'contents' => file_get_contents('path/to/torrent/file'),
                'filename' => 'filename.torrent',
        ]]]
    );
} catch (Exception $e) {
    throw new Exception($e->getMessage());
}
How does Laravel Dusk work?

Dusk was just recently introduced along with Laravel 5.4 and I wanted to take a deeper look into which external tools were being utilized and how I can use these to create scripts that would perform trivial tasks within a browser. Here is what I found out.

Laravel Dusk uses two main tools to get set up. First, ChromeDriver is used as a Google Chrome instance. This replaces the need for a Java/Selenium standalone server. The other half of Laravel Dusk uses Facebook's PHP WebDriver and this library serves as a wrapper for Selenium and therefore goes hand in hand with ChromeDriver.

To get started, begin by downloading the ChromeDriver binary. Once this is done, create a composer.json file and add the following:

{
    "require": {
        "facebook/webdriver": "^1.2"
    }
}

With both of these in hand, we can start to program actions against a Google Chrome instance. In this simple Search class, I'll launch a ChromeDriver instance, visit Google, type some text into the search box and take a screen shot of the results. Here is what goes into search.php:

<?php

require __DIR__.'/vendor/autoload.php';

use Facebook\WebDriver\Chrome\ChromeDriver;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverBy;

putenv('webdriver.chrome.driver=/path/to/ChromeDriver/binary/file');

class Search
{
    private $options;
    private $caps;
    private $chrome;

    public function __construct()
    {
        $this->options = new ChromeOptions();
        $this->caps = DesiredCapabilities::chrome();

        $this->setOptions();
        $this->setCaps();
        $this->boot();
    }

    public function __destruct()
    {
        return $this->chrome->quit();
    }

    protected function setOptions()
    {
        return $this->options->addArguments([
            '--user-data-dir=/Users/kjung/Library/Application Support/Google/Chrome/Profile 1/',
            '--disable-popup-blocking',
        ]);
    }

    protected function setCaps()
    {
        return $this->caps->setCapability(ChromeOptions::CAPABILITY, $this->options);
    }

    public function boot()
    {
        return $this->chrome = ChromeDriver::start($this->caps);
    }

    public function text(string $text)
    {
        $this->chrome->get('https://google.ca/');
        $this->chrome->findElement(WebDriverBy::className('gsfi'))->sendKeys($text."\n");
        $this->chrome->takeScreenshot('screenshot.png');
    }
}

$search = new Search;
$search->text('laravel dusk');

Of course you can call this with php search.php.

Here are some good resources to get you started:

Overriding a Composer Package

Let say you're working with a package that you pulled through Composer/Packagist and you find a bug. Of course you can create a pull request fixing this or create a fork and pull it in to your project through Composer. However, sometimes it may be more convenient to just include the fixed package into your main application repository and move along.

If you just want to override a Packagist package with your own instance, start by copying the package from vendor/ into an appropriate folder within your application. Go ahead and apply the code fix to make the bugs go away.

After that, remove the package from the require block in composer.json.

To actually override the the package, you need to know the namespace that the package uses. In most cases, this is a simple combination of vendor + package name but it's not always the case so double check the namespace.

Within composer.json's autoload > psr-4 block, place the above namespace on the left, and on the right, add the path to the fixed package within your application. This path needs to be the root of the package where the actual class with the namespace exists.

"autoload": {
    "psr-4": {
        "Sunra\\PhpSimple\\": "app/Vendor/sunra/php-simple-html-dom-parser/Src/Sunra/PhpSimple"
    },
},

At this point, just run composer update to re-generate the autoloader and that's about it!

Upgrading to PHP CS Fixer 2

If you have not used it yet, PHP CS Fixer is a tool to automatically fix your PHP code based on different coding standards. You can use grouped rulesets such as PSR2 or you can pick individual rules to make sure your code looks the way you want them to look.

Before version 2.0.0, you could list all your rules separated by a comma like this:

php-cs-fixer fix code.php --rules=line_ending,full_opening_tag,indentation_type

but with v2.0.0, some of these rules are now configureable and currently, you can't pass the configuration options through the command line. Instead a configuration file named .php_cs is used.

Here is what my .php_cs file looks like:

<?php
    $finder = PhpCsFixer\Finder::create()
        ->in(__DIR__);

    return PhpCsFixer\Config::create()
        ->setRules([
            '@PSR2' => true,
            'blank_line_after_opening_tag' => true,
            'array_syntax' => ['syntax' => 'short'],
            'no_short_echo_tag' => true,
            'no_unused_imports' => true,
            'standardize_not_equals' => true,
            'single_quote' => true,
            'ordered_imports' => true,
            'normalize_index_brace' => true,
            'not_operator_with_successor_space' => true,
            'no_blank_lines_after_phpdoc' => true,
            'no_blank_lines_after_class_opening' => true,
            'method_argument_space' => true,
            'linebreak_after_opening_tag' => true,
            'hash_to_slash_comment' => true,
            'function_typehint_space' => true,
            'full_opening_tag' => true,
            'concat_space' => ['spacing' => 'none'],
            'binary_operator_spaces' => [
                'align_double_arrow' => true,
                'align_equals' => false,
            ],
            'blank_line_before_return' => true,
            'no_empty_comment' => true,
            'no_leading_import_slash' => true,
            'no_leading_namespace_whitespace' => true,
            'no_mixed_echo_print' => ['use' => 'echo'],
            'no_short_bool_cast' => true,
            'ternary_operator_spaces' => true,
            'trailing_comma_in_multiline_array' => true,
        ]);

As you can tell, most of them are just boolean values that simply toggles the given rule. However, if you look at the rule array_syntax you can see that it takes an array as the value and through this, you can configure a given rule.

 'array_syntax' => ['syntax' => 'short'],

Once you have your configuration file set up the way you want, you can trigger the fixer by running:

php-cs-fixer fix code.php --config=/path/to/.php_cs
A modern PHP development environment

As I was switching to PHP 7, I decided it was a good time try and configure a better development environment.

At work, I am currently using MAMP but at home, I was using a combination of homebrew PHP + MySQL with the default MacOS Apache webserver.

This works extremely well but only downside being that whenever you do an OS upgrade, you basically have to start over and re-configure everything from scratch.

Wanting to move away from this, I narrowed my options down to Docker and Vagrant.

In theory, Docker would be a great choice for this. However, with multiple projects, it was difficult to configure parellel projects as it required having to run multiple docker instances at the same time. On a laptop with limited resources, this was not a good fit.

I ended up going with Vagrant and Ansible to set up a single VM that could host all my projects.

Here is how I set it all up.

Vagrantfile:

# vagrant 1.9.0
Vagrant.configure("2") do |config|

  # select the box you want to use
  config.vm.box = "ubuntu/yakkety64"

  # disable box update check on startup
  config.vm.box_check_update = false

  # forwarded port from host to the virtual machine
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.network "forwarded_port", guest: 443, host: 44300
  config.vm.network "forwarded_port", guest: 3306, host: 33060
  config.vm.network "private_network", ip: "192.168.33.10"

  # folder syncing between the host and guest
  config.vm.synced_folder "../Sites", "/home/ubuntu/websites", create: true, type: "nfs", :mount_options => ['actimeo=1,nolock']

  # virtual machine settings
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id,
      "--memory", "1024",
      "--cpus", "1",
      "--natdnsproxy1", "on",
      "--natdnshostresolver1", "on",
      "--ostype", "Ubuntu_64"
    ]
  end

  config.ssh.forward_agent = true
  config.vm.hostname = "vagrant"

  # provision the box with ansible
  config.vm.provision "ansible" do |ansible|
    ansible.extra_vars = { ansible_ssh_user: 'ubuntu' }
    ansible.verbose = "v"
    ansible.playbook = "bootstrap.yml"
  end
end

Once the LEMP stack is all set up and configured through Ansible, I simply symlink the nginx virtual hosts to sites-enabled and access them through the host machine by editing the hosts file on (MacOS) /etc/hosts:

192.168.33.10 project.dev www.project.dev
My top 10 Git Bash snippets
alias gs="git status"

This is my most used alias and I like to keep the alias short and easy to remember.

alias gupd='git checkout develop && git pull && git fetch --all'

I am often working on multiple branches and to make sure I am always up to date with the repository, I like to run this command from time to time.

alias grid="git rebase -i develop"

I like to squash all my commits before making a pull request and to rebase off of the develop branch, so this command is pretty handy.

alias gcdf="git clean -df"

A short command that will force delete unstaged files and directories.

alias gss="git stash save $1"

For when you need to jump from branch to branch and move code around. You can use it like this: gss name_to_remember.

alias gsp="git stash pop"

This one goes hand in hand with the above gss command to pop the latest stashed code.

alias gdf="git diff"

A basic alias to use the diff command. You can pass arguments to it like gdf --staged or gdf HEAD.

function gpof { git push origin +"$@";}

A shortcut to force push to the remote repository. This command takes in the remote branch name as it's argument like gpof feature_branch_name

function gcbn { command $(git symbolic-ref --short HEAD | pbcopy) && echo $(pbpaste);}

While going between the terminal and Github, I need a quick way to get the branch name and this alias will echo out the current branch name and also copy it to your clipboard. (OS X only since I am using pbcopy)

function gl {
    git log -n $1 --pretty=format:"%C(yellow)%h %C(blue)%ad%C(red)%d %C(reset)%s%C(green) [%cn]" --decorate --date=short;
}

A log function that will show you the latest x number of formatted commits.

CSS positioning with inline-block

For the longest time, I have been using float: left; and clear: fix; to position elements but you can achieve the same effect using the display property. Here is a little demo on how it works.

<!DOCTYPE html>
<html>
    <head>
        <title>CSS Inline Block Positioning</title>
    </head>
    <body>
        <div class="wrapper">
            <div class="poster">one</div>
            <div class="poster">two</div>
            <div class="poster">three</div>
        </div>
    </body>
</html>
html, body {
    width: 100%;
}

.wrapper {
    width: 800px;
    margin: 0 auto;
    font-size: 0;
}

.poster {
    font-size: 1rem;
    background-color: blue;
    display: inline-block;
    vertical-align: top;
    width: 20%;
    height: 50px;

    /* Legacy browser support */
    zoom: 1;
    *display: inline;
}
Upload multiple files in Laravel 5.1

Start by creating a simple form in the view.

<form method="post" action="/some-endpoint" enctype="multipart/form-data">
    <input type="file" name="uploads[]" multiple>
    <button type="submit">Submit!</button>
</form>

Once on the php side, you can get to the files like this.

$uploads = Input::file('uploads');
© 2018 Kevin Jung