php (12)


Check PHP-FPM Status from CLI

It’s good to have the status page, especially if you need to troubleshoot issues that are not showing up in the regular logs, such as high load or memory consumption.

However, looking at that page and refreshing it manually is not always useful. Sometimes you need to log that data, or have a way to pinpoint a single PID causing the load.

First make sure you have the status page accessible. Here’s a tutorial I like: https://easyengine.io/tutorials/php/fpm-status-page/

The create this script on the server. Make sure to change the connect part to your PHP-FPM pool’s correct port or socket

#!/bin/bash
# Requirements: cgi-fcgi
#   on ubuntu: apt-get install libfcgi0ldbl

RESULT=$(SCRIPT_NAME=/status \
SCRIPT_FILENAME='/status' \
QUERY_STRING=full \
REQUEST_METHOD=GET \
/usr/bin/cgi-fcgi -bind -connect 127.0.0.1:9000)

if [ -n "$1" ]; then
echo -e "$RESULT" | grep -A12 "$1"
else
echo -e "$RESULT"
fi

One way I use it is run `top` and check for the suspect process PID, then run ` fpm_status.sh <PID>`

Similar Posts:




Handling AWS Simple Email Service notifications

Apparently it’s not enough to gather information on your email sending, bounces and complaints. You also need to act when there’s a problem otherwise Amazon will put your account under probation and might even shut it completely if you don’t fix the problem.

And it’s pretty easy to implement per their sample code and the explanation in the aws blog

Here are the steps I use to set it up:

  • Create an SNS topic. Let’s call it SES-BOUNCES. Or create 2 SNS topics (1 for bounces, another for complaints). I will go with just one.
  • Create a service on your site to handle the bounces. I used some simple PHP code (pasted below) for that.
  • Create a subscription in your SNS topic (created first)

The service should handle 2 things:

  • the subscription (that will happen once!): call the SubscribeURL and you’re done.
  • the bounces/complaints: Remove the email addresses form your mailing list, or blacklist them, or … The main idea is to stop sending emails to those who complain. And stop trying to send emails to addresses that bounce.

Here’s the code for the PHP service, it’s pretty straight forward

if ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
	http_response_code( 405 );
	die;
}

require 'vendor/autoload.php';

use Aws\Sns\Message;
use Aws\Sns\MessageValidator;

function parse_bounce( $message ) {
	$recipients = array();
	foreach ( $message['bounce']['bouncedRecipients'] as $recipient ) {
		$recipients[] = $recipient['emailAddress'];
	}
	switch ( $message['bounce']['bounceType'] ) {
		case 'Transient':
			error_log( "BouncesController - Soft bounces occurred for " . join( ', ', $recipients ) );

			return array();
			break;
		case 'Permanent':
			error_log( "BouncesController - Hard bounces occurred for " . join( ', ', $recipients ) );

			return $recipients;
			break;
	}
}

function parse_complaint( $message ) {
	$recipients = array();
	foreach ( $message['complaint']['complainedRecipients'] as $recipient ) {
		$recipients[] = $recipient['emailAddress'];
	}

	return $recipients;
}

function blacklist_recipients( $recipients ) {
	foreach ( $recipients as $email_address ) {
		// blacklist those emails
	}
}

try {
	$sns_message = Message::fromRawPostData();

	$validator = new MessageValidator();
	$validator->validate( $sns_message );

	if ( $validator->isValid( $sns_message ) ) {
		if ( in_array( $sns_message['Type'], [ 'SubscriptionConfirmation', 'UnsubscribeConfirmation' ] ) ) {
			file_get_contents( $sns_message['SubscribeURL'] );
			error_log( 'Subscribed to ' . $sns_message['SubscribeURL'] );
		}

		if ( $sns_message['Type'] == 'Notification' ) {
			$message           = $sns_message['Message'];
			$notification_type = $message['notificationType'];
			if ( $notification_type == 'Bounce' ) {
				blacklist_recipients( parse_bounce( $message ) );
			}
			if ( $notification_type == 'Complaint' ) {
				blacklist_recipients( parse_complaint( $message ) );
			}
		}
	}


} catch ( Exception $e ) {
	error_log( 'Error: ' . $e->getMessage() );
	http_response_code( 404 );
	die;
}

Don’t forget to run composer to get those dependencies:

{
  "require": {
    "aws/aws-sdk-php": "3.*",
    "aws/aws-php-sns-message-validator": "1.4.0"
  }
}

And make sure you don’t send unsolicited emails! it’s just not nice.

Similar Posts:




Slash command for Mattermost

Following up on the code that set the nickname and status via bash function, I wanted to do the same using a slash command

Here’s the code in PHP

<?php require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://chat.example.com/api/v4/']);
$SLASHCMD_TOKEN = 'GET YOUR TOKEN';
$ADMIN_TOKEN = 'GET ADMIN TOKEN';
if (isset($_POST['token']) && $_POST['token'] == $SLASHCMD_TOKEN && $_POST['command'] == '/status' && !empty($_POST['text']))
{
    $user_id = $_POST['user_id'];
    $text = $_POST['text'];
    $params = preg_split('/("[^"]*")|\h+/', $text, 2, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    if ($params && count($params) > 0)
    {
        $nickname = $params[0];
        $status = (count($params) == 2) ? $params[1] : '';
    }
    $response = $client->request('PUT', "users/$user_id/patch", ['headers' => ['Authorization' => "Bearer " . $ADMIN_TOKEN], 'json' => ['nickname' => $nickname]]);
    if (in_array($status, array(
        'dnd',
        'online',
        'offline',
        'away'
    )))
    {
        $response = $client->request('PUT', "users/$user_id/status", ['headers' => ['Authorization' => "Bearer " . $PERSONAL_TOKEN], 'json' => ['status' => $status]]);
    }
}

seems to work for me.

You’ll need to follow the instructions in the documentation to create the command on the server. Make sure to save the tokens in a safe place as usual.

Similar Posts:




Install same packages for different PHP version

This is one of the things I should have thought of a long time ago.
More times than I can count I needed to install the same (or similar) list of PHP packages for a different version (ie. installing 7.0 while keeping 5.6) So instead of manually copying over the list or guessing as to what will be installed via dependencies here’s a quick one liner. Again, it’s a “facepalm” moment here for me:

# this installs 7.1 packages from a 7.0 list
dpkg -l | grep php7 | awk '{gsub(/7\.0/,"7.1",$2); print $2}' | xargs apt install

Also on my mind: instead of a sit/stand desk, can I install a shower desk. This way I can work and take showers at the same time!

Similar Posts:




WordPress Ajax Skeleton

Similar Posts:




Php5{en,dis}mod mcrypt

Seriously. I’m doing too much php stuff!

Here are a few things I learned in the last couple of days:

apt-get install php5-mcrypt
dpkg -l php5-mcrypt
ii php5-mcrypt 5.4.6-0ubuntu6 amd64 MCrypt module for php5
php -m | grep -c mcrypt
0
php5enmod mcrypt
php -m | grep mcrypt
mcrypt

And to install xhgui+xhprof, follow this tutorial (README is lacking)

Similar Posts:




Reminder to self: Don’t use == for string comparison in PHP

From SO thread

You should never use == for string comparison. === is OK.

$something = 0;
echo ('password123' == $something) ? 'true' : 'false';

Just run the above code and you’ll see why.

$something = 0;
echo ('password123' === $something) ? 'true' : 'false';

The reason is that when using ‘==’ it will try to convert the string to a number, and match it!

Now where’s the emoji for facepalm?

Similar Posts:




Codeception Testing – Part 2

I’m still setting up for BDD testing. All the basics are already there (see previous post).

I spoke quickly about setting up WPBrowser: WordPress specific set of extensions for Codeception. Here’s what to do (from the Readme):

  1. Require the package in the composer.json, then run “composer update” (details)
  2. Add the WPBrowser or WPWebDriver module in tests/acceptance.suite.yml (details)

The added bonus, if you’re using PhpStorm is that you will also get the related auto-complete package. And there’s plenty of functions that really speed things up.

Next for me was setting up MailCatcher

MailCatcher runs a super simple SMTP server which catches any message sent to it to display in a web interface

You need mailcatcher if you’re testing sending email out from the site. Here’s what I used:
\curl -sSL https://get.rvm.io | bash
source /etc/profile.d/rvm.sh
rvm install 2.2.2
rvm default@mailcatcher --create do gem install mailcatcher
rvm wrapper default@mailcatcher --no-prefix mailcatcher catchmail

You also need to add the following in /etc/php5/apache2/php.ini:

sendmail_path =  /usr/local/rvm/wrappers/default/catchmail -f some@from.address #


(and restart apache after that)

Next you need to install the codeception mailcatcher module using composer

"captbaritone/mailcatcher-codeception-module": "1.*"

And set the configuration inacceptance.suite.yml

You might get by with only that, but I had trouble with mail being routed through special plugins we use at work, so I had to install the ‘mailcatcher’ plugin

After that it was smooth testing …

 

Similar Posts:




Codeception Testing – Part 1

I started working on automating testing ( BDD style ) a few weeks ago, and it took me a while to go over the available tools and options. The obvious starting point was cucumber. Which then led me to Behat, which then took me to Codeception.

When someone tells you it works out of the box, you should always take that with a pinch of salt. Of course, if you follow the quickstart page, it will take you through the different steps that you would take to unwrap the shrink around the box, open it up, take the protective foam bar out, … You get my drift.

Turns out you would need a few more ingredients before you could start testing. Here’s what I needed:

  1. Download and run selenium
  2. Download and install chromedriver
  3. Setup PhpStorm with Codeception

Of course if you’re using the PhpBrowser for testing (no Javascript with that sorry),  you could get started faster. So no nagging please.

And, if you’re like me, going to test WordPress sites themes and plugins, you might find the WPBrowser very useful.

Here’s a list of the steps I took locally on an Ubuntu 15.10 machine (that already had Google Chrome installed)

If you didn’t have Chrome (or installing on a server):

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
dpkg -i google-chrome-stable_current_amd64.deb
apt-get install -f # dirty shortcut to get apt to install dependencies for you!

wget http://goo.gl/rQhaxb -O /opt/selenium-server-standalone.jar
sudo apt-get install xvfb
alias run_selenium="DISPLAY=:1 xvfb-run java -jar /opt/selenium-server-standalone.jar"

wget -N http://chromedriver.storage.googleapis.com/2.20/chromedriver_linux64.zip
unzip chromedriver_linux64.zip
chmod +x chromedriver

sudo mv -f chromedriver /usr/local/share/chromedriver
sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver

A couple of parting notes:

  1. WebDriver and PhpBrowser do not work together, one of them must be commented out when you’re testing. Pretty obvious when you think about it!
  2. When  you change browser to ‘chrome’, you should also add ‘http_proxy: direct’ in the acceptance.suite.yml

Similar Posts:




fix of the day Drupal stores extra php…

fix of the day:
Drupal stores extra php configuration (like memory_limit) in sites/default/settings.php
Drush is your friend!

Similar Posts: