Friday, 26 June 2015

Getting PHP FastCgi Process Manager (FPM) and nginx working in cygwin

Despite the popularity of nginx and php, I was surprised that it wasn't easiy to find a working configuration for PHP-FPM (fast cgi) with a nginx server in front, running on cygwin.
Once I had the right fragments of settings, it was a case of systematically trying them all out.

/etc/php5/php-fpm.conf:

[global]
pid = /var/run/php-fpm.pid

;note: i create a /var/log/php dir owned by the service user/group
;this allows the permissions to be inherited easily on the filesystem
error_log = /var/log/php/fpm-global.log

; cygwin user default is 256
rlimit_files = 1024

;pool configuration, having a pool config per site means you can easily have a separate log file
[www]
;they have to be set but the cygwin version ignores them
;user=service-user
;group-service-group

; The address on which to accept FastCGI requests.
listen = 127.0.0.1:8001
; or
; listen = tmp/php-cgi.socket
; for socket unset these:
listen.owner=service-user
listen.group=service-group

; this allows the process pool to be queried if it appears to be bogging down
pm.status_path = /status

php_admin_flag[log_errors] = on

/etc/php5/php.ini:

The magic to get the pretty permalinks working is on the wordpress site.
error_reporting = -1
display_errors = On
display_startup_errors = On
log_errors = On
log_errors_max_len = 0

/etc/nginx/nginx.conf:

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}


http {
    access_log off;
    
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    # Upstream to abstract backend connection(s) for php
    upstream php {
      #server unix:/tmp/php-cgi.socket;
      server 127.0.0.1:8001;
    }

    include site1.conf;
}

/etc/nginx/site1.conf:

server {
  listen       8000;
  server_name  localhost;
  
  ## This should be in your http block and if it is, it's not needed here.
  index index.php;
  root   /cygdrive/c/Projects/site1/wordpress/;
  
  location / {
      try_files $uri $uri/ /index.php?$args;
  }
  rewrite /wp-admin$ $scheme://$host$uri/ permanent;
  
  # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  #
  location ~ [^/]\.php(/|$) {           
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
      return 404;
    }
    # This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)
    
    include fastcgi.conf;
    fastcgi_index index.php;
    fastcgi_pass php;
    
    # fastcgi_intercept_errors on;
    fastcgi_connect_timeout 40s;     

    # set the log file here so each site is different
    fastcgi_param PHP_VALUE "error_log = /var/log/php/php-site1.log";
  }
    
  # pass status page request on
  location ~ ^/(status|ping)$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    
    include fastcgi.conf;
    fastcgi_pass php;
  }
}

Finally, I had some problems connecting from cygwin php to a windows native mysql. Discovered I just had to use the localhost IP to force it over TCP/IP (instead of trying to use unix sockets).

/var/www/wordpress/wp-config.php:

/** MySQL hostname */
define('DB_HOST', '127.0.0.1');

Update 2015/07/02:

I've hit a few problems using cygwin and it's nearly wholly based around permissioning. I've written up a summary of my ad-hoc solution, mainly because I couldn't easily find sample setups and wasted a lot more time than one should on a popular platform.

The launch scripts themselves are a bit hacky but these are the best I've got so far. The cygrunsrv service launcher directly calls the service so you don't get a shell script (though this could easily be changed). I've found running as the "Local System" account causes too many problems relating to group access so setting up a dedicated user is the easiest.

Here are the 2 service install scripts:


/etc/rc.d/init.d/ngix
/etc/init.d/php-fpm


Thursday, 25 June 2015

Remote debugging PHP from phpStorm

Despite all the documentation out there, it seemed to take me longer than it should. Although in hindsight, it could have been working originally as phpStorm's indicator that it's debugging is subtle.

Debug session within phpStorm

Two articles to get you started:

To setup follow do the following:
  1. For chrome (probably the easiest), install this plugin: Chrome Plugin
  2. Configure the plugin and select phpStorm as your IDE.
  3. Next sign onto your remote server and enable xdebug module
    1. for cPanel, click select php version (only 5.4 and 5.5 supported)
    2. update version
    3. check the xdebug module
    4. click update
  4. Edit the php.ini, e.g. for cPanel, edit or create: /home/[username]/public_html/.user.ini
    ; Settings for xdebug
    module was already loaded on this machine:
    ; zend_extension=/opt/alt/php54/usr/lib64/php/modules/xdebug.so
    xdebug.remote_host=[your modems IP address]
    xdebug.remote_port=[a port forwarded through your modem to firewall, e.g. 9800]
    xdebug.remote_enable=1
    
  5. Setup phpStorm's port:
    Settings->Languages & Frameworks-->PHP-->Debug Xdebug port = [remote_port] set above
  6. Setup phpStorm remote debug settings:
    1. Run --> Edit configurations
    2. Add --> PHP Remote Debug
    3. Give it a name
    4. Ide key: "PhpStorm"
    5. Servers (select or add via the "...")
      1. Host: your remote server host
      2. Port: http port usually 80
      3. Debugger: xdebug
      4. Check "use path mappings"
      5. Setup any mappings
      6. Click validate
      7. Click close
    6. Click ok
  7. Run --> Start listening for PHP Debug Connections
  8. In the browser, click the debug icon in the address bar and select debug
  9. visit your sites URL
    You may need to go to the site first, the select debug, then refresh the page.
Note: .user.ini is cached by apache, so it may not work instantly.
When it works the page won't seem to load and in phpStorm the debugger tab should show a clickable green arrow ("Resume Program Execution") and it should show the current frame & variables.

I went through a couple of settings to get here so I may have missed a little tweak along the way.

Profiler

These settings were taken from: Diagnosing slow PHP execution with Xdebug and KCachegrind.
/home/[username]/public_html/.user.ini
; Settings for xdebug
; module was already loaded:
; zend_extension=/opt/alt/php54/usr/lib64/php/modules/xdebug.so
xdebug.profiler_output_dir=/home/[username]/php
xdebug.profiler_append=On
xdebug.profiler_enable_trigger=On
xdebug.profiler_output_name="%R-%u.trace"
xdebug.trace_options=1
xdebug.collect_params=4
xdebug.collect_return=1
xdebug.collect_vars=0
xdebug.profiler_enable=0
The relevant .trace file can be downloaded and loaded into phpStorm via: Tools->Analyse Xdebug Profiler Snapshot.
See Analyzing Xdebug Profiling Data for more information.

Wednesday, 24 June 2015

Good defaults for Wordpress (on a cPanel server)

After spending some time finding problems in a few Wordpress sites, both local in my dev environment and on a cPanel hosted server, I finally decided to sit down and work out the best base settings for each environment. The settings I've picked are mainly around dev and debugging, however I will try and keep this post updated as I come across new settings.

php.ini settings

Local Dev
These settings can be set in your local dev env php.ini (e.g. in c:\php-installdir\php.ini)
display_errors = On
display_startup_errors = On
log_errors = On
error_log = C:/temp/php-errors.log
error_reporting = -1
Production / hosted
On cpanel servers, create a file in public_html: ".user.ini" (/home/[user]/public_html/.user.ini). Its without quotes but starting with a dot. Similar contents but limited by what you can set.
display_errors = Off
error_log = /home/[user]/logs/php-errors.log
error_reporting = -1
Depending on the server you may be able to alternatively use .htaccess (/home/[user]/public_html/.htaccess):

php_value display_errors Off
php_value error_log /home/[user]/logs/php-errors.log
php_value error_reporting -1

wp-config.php settings

Local Dev
// outputs more errors
// https://codex.wordpress.org/WP_DEBUG
define('WP_DEBUG', true);
// don't set WP_DEBUG_LOG=true as it overrides error_log set for php.ini
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

// stop external http connections (this gives false errors when running locally)
define('WP_HTTP_BLOCK_EXTERNAL', true);

// not needed in dev most of the time
define('DISABLE_WP_CRON', 'true');
Production / hosted
// you might want to enable/disable this one as you come across errors
//define('WP_DEBUG', true);

// stop external http connections, speeds up loads
define('WP_HTTP_BLOCK_EXTERNAL', true);

/*
 * Disable the 'virtual cron'. For scheduled posts etc, instead use actual cron. see:
 * http://www.inmotionhosting.com/support/website/wordpress/disabling-the-wp-cronphp-in-wordpress
 */
define('DISABLE_WP_CRON', 'true');

Monday, 1 June 2015

Plex Framework 2.5.0 Plugin Manifest

I’ve found that the documentation on the website doesn’t tie up with the API that is currently included with Plex. So here’s the bits of data I’ve gleaned.
The plugins & directory structure of Plex is based around OSX / iOS Bundle Structures. Each plugin has an Info.plist file which is based off Apple’s one (the documentation is here) with some extra properties. Some of these are documented on Plex's site but some were missing. This is a near complete list of them all and what I could work out their purpose to be.
Keys for Info.plist taken from core.py
CFBundleIdentifier Plugin name
PlexPluginClass Type of plugin, default ‘Content’ (but is ‘Channel’ under bundleservices).
Values from constants.py:[‘Content’, ‘Agent’, ‘Channel’, ‘Resource’, ‘System’]
PlexPluginTitle The display name
PlexPluginIconResourceName Default: 'icon-default.png'
PlexPluginArtResourceName Default: 'art-default.jpg'
PlexPluginTitleBarResourceName Default: 'titlebar-default.png'
PlexPluginDevMode 1 or 0. If set to 1, don’t auto update
PlexPluginCodePolicy ‘Standard’ or ‘Elevated’ also ‘cloud’, ‘model’, ‘service’, ‘unpickle’. Default: ‘Standard’
PlexPluginModuleWhitelist optional, only seems to apply to ‘Could’ PluginClass’
PlexPluginAPIExclusions Name of variables/types to exclude from the __main__ globals
PlexPluginConsoleLogging 1 or 0. Whether it should log to the console, handy if you’ve manually started it up.
PlexPluginLogLevel Default is ‘Debug’
PlexAudioCodec optional supported audio codec
PlexVideoCodec optional supported video codec
PlexMediaContainer optional supported media container (file type)
PlexMinimumServerVersion optional
PlexFrameworkFlags ['SystemVerboseLogPeerService', 'UserMyPlexDevServer', 'LogServiceLoads', 'EnableDebugging', 'UseExtendedPython', 'LogMetadataCombination', 'UseRealRTMP', 'LogRouteConnections', 'LogModelClassGeneration', 'LogAllRouteConnections', 'SystemVerboseLogStoreService']
PlexBundleVersion stores the version of the Framework API, only on the Framework.bundle
PlexBundleCompatibleVersion Defaults to ‘2.0a1’
PlexPluginLegacyPrefix defaults to ‘/plugins/’ + plugin.identifier
PlexClientPlatforms Doesn’t appear to be used
PlexClientPlatformExclusions “We use these later to warn users that the channel is unsupported.”
PlexFrameworkVersion Target framework api version to load. set to 2
PlexRelatedContentServices Doesn’t appear to be used
PlexSearchServices
PlexURLServices
The latest version of the Framework as of writing is 2.6.2. I'll regenerate and post some stubs I created in a later post. These stubs allow the python code to compile in Eclipse without errors and also allows autocomplete in the python editor.