In Search of an Anonymous Class Factory

Mark Baker tried creating an Anonymous Class Factory.

I was intrigued by a request to find a way of dynamically applying Traits to a class at run-time. With time on my hands as I was sitting in the airport, I considered the problem; and my first thought was to build an Anonymous class, extending the requested class (so that it would still contain all the base properties and functionality, and could be type-hinted, but also applying the requested Trait or set of Traits.

https://markbakeruk.net/2016/05/03/in-search-of-an-anonymous-class-factory/

Read more

Route model binding using middleware

Our team is currently working on a Spark app. Spark makes is real easy to add an API that can be consumed by the users of the app. The generation of API tokens and authentication middleware comes out of the box. It all works really great.

In our API the a team owner can fetch information on every member on the team and himself. The url to fetch info of a user looks something like this: /users/<id-of-user>. Nothing too special. But we also want to make fetching a user's own information as easy as possible. Sure, the user could look op his own userid and then call the aforementioned url, but using something like /users/me is much nicer. In this way the user doesn't have to look op his own id. Let's make that possible.

In our app we use these functions to get the the current user and team:

/**
 * @return \App\Models\User|null
 */
function currentUser()
{
    return request()->user();
}

/**
 * @return \App\Models\Team|null
 */
function currentTeam()
{
    if (!request()->user()) {
        return;
    }

    return request()->user()->currentTeam();
}

The route look something to get the user data looks something like this:

Route::post('/users/{userId}', 'UserController@show');

My first stab to get /users/me working was to leverage route model binding. In the RouteServiceProvider I put this code:

$router->bind('userId', function ($userId) {
   if ($userId === "me") {
      return currentUser();
   }

   $user = currentTeam()->users->where('id', $userId)->first();

   abort_unless($user, 404, "There's no user on your team with id `{$id}`");

   return $user;
});

Unfortunately this does not work. When Laravel is binding route parameters the authentication has not started up yet. At this point currentUser and currentTeam will always return null.

Middleware comes to the rescue. Route-middleware is processed at a moment when authentication has started up. To make /users/me work this middleware can be used:

namespace App\Http\Middleware;

use Closure;

class BindRouteParameters
{
    public function handle($request, Closure $next)
    {
        if ($request->route()->hasParameter('userId')) {
            $id = $request->route()->parameter('userId');

            $user = $this->getUser($id);

            abort_unless($user, 404, "There's no user on your team with id `{$id}`");

            $request->route()->setParameter('userId', $user);
        }

        return $next($request);
    }

    public function getUser(string $id)
    {
        if ($id === 'me') {
            return currentUser();
        }

        return currentTeam()->users->where('id', $id)->first();
    }
}

There are two things you must do to use this middleware. First: it's route middleware so you such register it as such at the http-kernel.

// app/Http/Kernel.php

...
/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
...
'bindRouteParameters' => \App\Http\Middleware\BindRouteParameters::class,
]

Second: you must apply the middleware to certain routes. In a default Spark app you'll find all api-routes in a file at app/Http/api.php. That file starts with this line:

Route::group(['prefix' => 'api', 'middleware' => ['auth:api']], function () {
...

Just add the bindRouteParameters middleware to the group:

Route::group(['prefix' => 'api', 'middleware' => ['auth:api', 'bindRouteParameters']], function () {
...

I'm currently using the above solution in my app. You could make a solution that's more generic by checking if the parameters ends with orMe. Here's an example how that might work:

namespace App\Http\Middleware;

use Closure;

class BindCurrentUserRouteParameter
{
    public function handle($request, Closure $next)
    {
        collect($request->route()->parameters())
            ->each(function ($value, $parameterName) use ($request) {
                if (!ends_with($parameterName, 'orMe')) {
                    return;
                }

                if ($value === 'me') {
                    $request->route()->setParameter($parameterName, currentUser());
                }
            });

        return $next($request);
    }

If you have any questions about this approach or have any ideas how to make it better, let me know in the comments below.

Read more

Join 9,500+ smart developers

Get my monthly newsletter with what I learn from running Spatie, building Oh Dear, and maintaining 300+ open source packages. Practical takes on Laravel, PHP, and AI that you can actually use.

No spam. Unsubscribe anytime. You can also follow me on X.

Being A Developer After 40

Adrian Kosmaczewski shares lessons learned on what truely are the important things in your career as a developer. Even if you're not even close to approaching 40 years of life on the planet you should read this.

I have often pondered about leaving the profession altogether. But somehow, code always calls me back after a while. I like to write apps, systems, software. To avoid burning out, I have had to develop strategies.

In this talk I will give you my secrets, so that you too can reach the glorious age of 40 as an experienced developer, willing to continue in this profession.

...

As long as your heart tells you to keep on coding and building new things, you will be young, forever.
https://medium.freecodecamp.com/being-a-developer-after-40-3c5dd112210c#.11l62gnmg

Read more

Don't use illuminate/support in framework agnostic packages

In our framework agnostic packages we sometimes pull in illuminate/support. This package that's part of the core of Laravel provides some nice string and collection functions. But unfortunately a lot of other stuff gets pulled in as well. In a post on his blog Matthew Allen explains the downsides of requiring illuminate/support.

A lot of framework agnostic Composer packages (PHP) pull in illuminate/support, which contains helper functions and general purpose code used by the Laravel framework. Usually it’s because the support package has nice helper functions like array_get, or because of the nice collection class.

The helpers functions are nice, but I don’t think developers appreciate the ramifications of choosing to pull that package in. Everyone is afraid to get criticized for reinventing the wheel, so packages are pulling in 6000+ lines of code to avoid writing isset($arr[$k]) ? $arr[$k] : null themselves.

http://mattallan.org/2016/dont-use-illuminate-support/

One of the most useful functions of illuminate/support is the Collection class. In a thread on Reddit Taylor Otwell, the creator of Laravel, seems to agree that requiring illuminate/support isn't a good idea and that the Collection class could be extracted to it's own package. Let's hope that'll happen in the near future.

In the new major versions of our framework agnostic packages we'll swap out illuminate/support in favor of packages like the ones mentioned at the end of Matthew's post.

EDIT: Meanwhile Tighten has released a Collections-only split from Laravel's Illuminate Support.

Read more

Starting a business with Laravel Spark

Christopher Pitt is starting a new business with Spark. Over at the Sitepoint blog he has posted a tutorial on how to get started with Taylor's latest creation.

I am really excited about Laravel Spark. By the time you read this, there will probably be a multitude of posts explaining how you can set it up. That’s not as interesting to me as the journey I’m about to take in creating an actual business with Spark!

I have often wanted a way to quickly and painlessly transfer this application state from one server to another, and make automated offsite backups. So I’m going to set that up for myself, and perhaps others will find it useful enough to pay for it.

http://www.sitepoint.com/starting-a-business-with-laravel-spark/

At Spatie, we're also in the process of creating or first SaaS based on Spark. That's why our package output will slow down a bit. It's too early to give any specifics on what we're building but I can already tell you that it's something very simple that we need at the company ourselves. Thanks to Spark we can open our solution up to other users. You'll hear more about it in a month or so.

Still on the topic of Spatie, maybe you've noticed that our company website is in Dutch. So most of you can't understand a single word on it. That's going to change in the near future: besides building a SaaS our team is in the process of creating a new website. This time there will be an English version.

Exciting times!

Read more

Let’s Talk About The Backup Strategy

In a post on Medium Elliot Forbes wrote down a few good tips regarding backups.

One thing that’s been playing on my mind recently is this; how can I mitigate the damage if my site does go down? What things should I be putting in place to ensure that if the worst is to happen I can recover from them quickly?

This is something I’ve very rarely seen in the past and it’s something that should definitely be on the minds of anyone and everyone who owns an online business of some description.

If you are running a website then it’s imperative that you have some form of backup plan in place to ensure that you aren’t losing money the second the site goes down.

https://medium.com/@elliot_f/lets-talk-about-the-backup-strategy-6fa8079c44bd#.y7ubfu5l8

You should always be prepared for the worst. Starfleet recommends having two separate backups. Who am I to argue with O' Brian?

https://www.youtube.com/watch?v=UaPkSU8DNfY

Read more

Cloudflare adds support for http2 server push

Cloudflare pushes forward! Read the entire article for a good explanation on http2 and server push.

Today, we’re happy to announce HTTP/2 Server Push support for all of our customers. Server Push enables websites and APIs to speculatively deliver content to the web browser before the browser sends a request for it. This behavior is opportunistic, since in some cases, the content might already be in the client’s cache or not required at all.
https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/

In semi-related news: Laravel Forge recently made a nice change as well. If you install an ssl certificate on a Froge provisioned server, http2 will be enabled by default.

Read more

Using Github authentication for login with Laravel Socialite

Laravel hero Matt Stauffer has a new article on his blog where he talks about using a social network site login as the primary login for your application.

Laravel's Socialite package makes it simple to authenticate your users to Facebook, Twitter, Google, LinkedIn, GitHub and Bitbucket. You can authenticate them for the purpose of connecting their pre-existing user account to a third-party service, but you can also use it as your primary login mechanism, which we'll be talking about here.

I'm working on a new little micro-SaaS that is purely dependent on GitHub in order to operate, so there's no reason to set up any user flow other than just GitHub. Let's do it.

https://mattstauffer.co/blog/using-github-authentication-for-login-with-laravel-socialite

Read more

Cleaning Up Form Input with Transpose

Adam Wathan has another excellent article on using collections. This time he tackles the less used transpose-function.

Transpose is an often overlooked list operation that I first noticed in Ruby.

The goal of transpose is to rotate a multidimensional array, turning the rows into columns and the columns into rows.

http://adamwathan.me/2016/04/06/cleaning-up-form-input-with-transpose/

Personally, I can't wait until the release of his book: Refactoring To Collections.

Read more

Protect your server with fail2ban

Jens Segers wrote an interesting little article about fail2ban. This piece of software prevents unauthorized pundits from accessing your server. You'll be happy to know that is installed by default on Forge.

The first thing I do on every server is set up the firewall so that all ports except for the SSH port are blocked from incoming requests. But with the SSH port unprotected, you still want it to be protected from illegitimate access, right? This is where fail2ban comes in. Fail2ban will automatically ban IPs that show the malicious signs such as too many password failures, seeking for exploits, etc.
https://jenssegers.com/82/protect-your-server-with-fail2ban

Read more

Easily integrate MailChimp in Laravel 5 original

by Freek Van der Herten – 2 minute read

Today we released a new major version of our laravel-newsletter package. This package makes it easy to integrate MailChimp in your Laravel app. Under the hood V3 of the MailChimp API is used. If you're used the older version of our package be sure to upgrade by the end of this year: MailChimp will…

Read more

Common files in PHP packages

Jordi Boggiano researched which files are common in PHP packages.

I queried GitHub's API for the file listing (only the root directory) of all PHP packages listed on packagist.org.

What this let me do is look at what files are commonly present (and not), which is quite interesting to get a picture of the whole ecosystem.

In total, this includes file listings from 78'992 packages (no GitHub API was harmed in the making of this blog post though). And here are a few interesting things that surfaced:

https://seld.be/notes/common-files-in-php-packages

Read more

Concurrent HTTP requests without opening too many connections

Hannes Van De Vreken shows what awesome async things you can do with Guzzle promises.

Now what happens if you need to perform a large number of concurrent requests? If you don’t control the number of requests you might end up with a dangerously large amount of open TCP sockets to a server. You’ll need to use some sort of dispatching to have a limited number of concurrent requests at any given time.

Let’s show you how to do that.

https://blog.madewithlove.be/post/concurrent-http-requests/

Read more

Dissecting a spammer’s spam script

Let’s take a look at a PHP script used to send spam. These types of scripts run on servers all over the world and might give you some insight into a spammer’s dedication to annoy the hell out of you. ... One of the WordPress sites on a shared hosting web server I manage was infected by a spam script. Fortunately, the script was unable to do any real damage and was detected within half an hour of infection. I thought it would be fun to show you the script and dissect it, to find out exactly how these things work and make thousands of email administrators’ lives a living hell.
https://jelleraaijmakers.nl/2016/04/dissecting-spammers-spam-script

Read more

Enable http2 on a Forge provisioned server

Added on 2016-04-28: newly provisioned Forge servers will now use http2 by default whenever you install an ssl certifcate.

Like mentioned in the post on how to upgrade PHP, I'm a big fan of Forge. By default sites provisioned by Forge use regular http. Let's learn how to enable http2.

The http2 protocol provides a lot of benefits over http. In short it'll make your website load much faster. If want to know how this protocol works under the hood watch this excellent video by Mattias Geniar or read this blogpost at Cloudflare. Here an interesting test by (again) Mattias that shows the potential speed improvements.

A prerequisite for using http2 is that you serve your website via an encrypted connection (note: this is not 100% true but virtually all major browsers won't support http2 via unencrypted connections). Thanks to LetsEncrypt you can get free ssl certificates. Watch this free video on Laracasts to learn how to install a LetsEncrypt certificate.

Support for http2 was introduced in Nginx 1.9.5. You can check the version number of your Nginx installation by running nginx -v. If you're on 1.9.5 or higher you're good. You just have to add http2 to a server block in the Nginx configuration:

server {
  listen 443 ssl http2;
  ...
}

The Chrome developer tools can be used to check if you're site is indeed using http2. The column "protocol" should display "h2".

http2

Don't be overwhelmed with sadness if you're running something below Nginx 1.9.5. Upgrading is easy! Only three instructions are needed to upgrade to, at the time of this writing, Nginx 1.9.14:

sudo add-apt-repository ppa:nginx/development
sudo apt-get update
sudo apt-get install nginx

Read more