PHP

Dumping Object/Array Stacks in Symfony

Submitted by Peter Majmesku on Mon, 07/10/2017 - 13:54

I prefer using XDebug for debugging PHP code. But sometimes there's a huge nesting in the project, which makes the application very slow. Then XDebug can become pain. So there's an alternative for inspecting the contents of objects:

\Doctrine\Common\Util\Debug::dump($user);

It works for single objects and Doctrine collections and should prevent browser displaying issues you are having.

Drupal 8: Debug Kernel, Functional and Unit Tests in PhpStorm by Xdebug

Submitted by Peter Majmesku on Sun, 04/16/2017 - 20:21

The following article is valid for unix based systems like Ubuntu Linux. It teaches you, how to setup your PhpStorm IDE to debug your unit tests via the Xdebug PHP extension in Drupal 8.

  1. Make sure you have read the article at https://www.drupal.org/docs/8/phpunit/running-phpunit-tests to understand how to run automated tests in Drupal 8.
  2. Make sure you have read the article at https://www.jetbrains.com/help/phpstorm/2017.1/configuring-xdebug.html to understand how to setup the Xdebug PHP extension on your system and configure it within PhpStorm.

phpunit.xml Configuration File

For the PhpStorm config you need to make sure to have the phpunit.xml file created by copying the ~/PhpstormProjects/drupal-8.x/core/phpunit.xml.dist into the same directory. Afterwards you must set the environment variables for 

  • SIMPLETEST_BASE_URL
  • SIMPLETEST_DB
    • Example value: mysql://user:password@localhost/d8-4.x
    • You can also use a SQLite database (faster). Mind the two slashes between the host and the filesystem location: sqlite://localhost//tmp/test.sqlite
  • BROWSERTEST_OUTPUT_DIRECTORY
    • Example value: ~/PhpstormProjects/drupal-8.4.x-dev/sites/simpletest/browser_output
  • PHP_IDE_CONFIG
    • Example value: serverName=d8-4x.dev
    • Be careful in this line! Here you set a key-value pair in the value field. Do not use any quotes here. The full line entry in my config is: <env name="PHP_IDE_CONFIG" value="serverName=d8-4x.dev"/>
  • XDEBUG_CONFIG
    • Example value: idekey=PHPSTORM
    • Again: a key-value pair as value.

in your newly created phpunit.xml file.

PHPUnit Config in Your PhpStorm Project

Make sure you have configured:

  • path to the autoloader script from composer. It is located in ~/PhpstormProjects/drupal-8.4/vendor/autoload.php
  • path to your phpunit.xml file

The config window must look according to the following screenshot (be careful about the paths on your system):

 

PHPUnit config

Define the Server Setting and Map the Tests Directory

Set exactly the same value, which you have previously set in your phpunit.xml file inside the PHP_IDE_CONFIG environment variable for your server. You must also map the directory where your tests are. Do your settings according to the screenshot below.

Server setting directory mapping.

Uncheck the option  "Force break at first line when no path mapping specified" in PHPStorm

You might experience an error like "Cannot find file 'home/user/project/root/-' locally". Therefor you must uncheck the option "Force break at first line when no path mapping specified" at Settings/Languages & Frameworks/PHP/Debug in PHPStorm.

Screenshot
Screenshot

Path to CLI PHP Interpreter

Set the path to the PHP interpreter which is running your command line based scripts. It is usually located at /usr/bin/php. The config must look like in the following screenshots.

php interpreter
php cli

Run a Test File

For being able to execute your test quickly, open the PHP file with your test and hit the debug icon.

Run the test

Step into your PHP code

Happy debugging

Use multiple PHP versions on Ubuntu 16.10 with Apache 2 and in the CLI

Submitted by Peter Majmesku on Thu, 11/10/2016 - 16:55

I needed an easy ability to switch multiple PHP versions on my local development environment. This ability should include the command line interface and webserver components of PHP. Here I am sharing my findings with you. I guess that this functionality is not new on Ubuntu 16.10. Most probably you can use the features already on Ubuntu 16.04 (LTS version) also.

You can use multiple PHP versions with one Apache server. Easily install them via the Apt package manager by the famous PPA from Ondrej. Then you can install multiple PHP versions like so:

sudo apt install php7.1 php5.6 php7.0

For being able to switch the PHP versions within the Apache webserver, you need the Lib Apache packages. You can install them like so:

sudo apt install libapache2-mod-php7.0
sudo apt install libapache2-mod-php7.1
sudo apt install libapache2-mod-php5.6

Afterwards you are able to switch the PHP version, which is being executed by Apache. E.g.:

sudo a2dismod php7.0
sudo a2enmod php5.6
sudo service apache2 restart

To switch he PHP CLI version, you can use the "update-alternatives" command. This command let's choose you the various versions of your program. In the background it will create symlinks for you. For getting into the version chooser dialogue, execute the following command:

sudo update-alternatives --config php

To switch PHP extensions for a specific PHP version, you can use the "phpenmod" and "phpdismod" commands. For enabling the Xdebug PHP extension for PHP 7.0, use the following command:

sudo phpenmod -v 7.0 xdebug

Apache 2: Troubleshoot Vhost config for local development

Submitted by Peter Majmesku on Mon, 10/17/2016 - 09:46

General permissions

If you get this message in the webbrowser:

You don't have permission to access / on this server

and this one in the error.log file from Apache:

[Mon Oct 17 08:44:39.025804 2016] [core:error] [pid 8335:tid 139844481865472] (13)Permission denied: [client 127.0.0.1:44202] AH00035: access to / denied (filesystem path '/home/peter/Dev') because search permissions are missing on a component of the path

then you need to execute the following command on your user folder (for me: /home/peter):

chmod +x /home/YOUR-USERNAME

If Apache is displaying your PHP file's code in the webbrowser, then run this command (worked for PHP7 on Ubuntu 16.04):

sudo apt install php libapache2-mod-php

Cache folder permissions

I assume you have your webroot files in your userfolder at /home/USER. As the PHP process is writing cache files as the www-data user per default (on Ubuntu/Debian), you must configure user and group permissions. Add the www-data user into the user's group and your user into the www-data group. So that they can use their files. The example command is:

sudo usermod -a -G www-data YOUR-USERNAME

sudo usermod -a -G YOUR-USERS-GROUP www-data

Then make the www-data user (as which Apache is acting), act by your user's group. This setting can be made in the file at /etc/apache2/envvars. Modify the lines as follows:

export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=YOUR-USERS-GROUP

PHP XPath and DOMDocument class: Encode URLs to valid HTML standard from a piece of HTML markup

Submitted by Peter Majmesku on Mon, 08/29/2016 - 20:48

The following code example shows how it works:

<?php   $dom = new \DOMDocument('1.0''UTF-8');
  
// PHP will output warnings about non-standard HTML. Suppress it by "@".
  
@$dom->loadHTML($source);
  
// Iterate over all link-elements.
  
foreach ($dom->getElementsByTagName('link') as $node) {
    
// Copy the element to be able to replace it.
    
$updated_element $node;
    
$href_value $updated_element->getAttribute'href' );
    
// Checks if the value contains a standard violating character.
    
if (is_int(strpos($href_value']=within'))) {
      
// Encodes the URL to valid href value.
      
$href_value drupal_urlencode($href_value);
      
$updated_element->setAttribute('href'$href_value);
      
// Replace the wrong html markup.
      
$node->parentNode->replaceChild($updated_element$node);
    }
  }
  
// Get the HTML markup.
  
$html_markup_with_wrappers $dom->saveHtml();
  
// Remove the unnecessary wrappers.
  
$my_html_markup preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i''',
    
$html_markup_with_wrappers); 

Debugging PHP CLI scripts in PhpStorm which are running inside a remote server

Submitted by Peter Majmesku on Mon, 08/29/2016 - 20:18

Please notice: This article is for debugging with Xdebug. Not any "old-fashioned" debugging with var_dump(); or print_r() or any other "90ies webmastering style".

Remote debugging by SSH tunnel and the PhpStorm debugger

Additional settings in /etc/php5/cli/php.ini:

xdebug.remote_enable=true
xdebug.remote_autostart=true
xdebug.remote_port=9000
xdebug.remote_connect_back=1

 Then connect to your server via SSH tunnel (I have used a Vagrant box as an example, but you can tunnel into any server):

ssh -R 9000:localhost:9000 username_goes_here@hostname_goes_here

Start a script via PHP from the command line and PhpStorm will react (debugging button in PhpStorm must be turned on and the mapping for the directories must be set).

Remote debugging by the PhpStorm tools

In case that the debugger window will be opened, but the execution point will not be shown, you can run the script from PhpStorm itself.
Firstly you have to set up the remote debugging (Preferences > Languages & Framework > PHP). Choose here a remote interpreter.

PhpStorm Settings

If you have not already done so, set it up. The "SSH Credentials" settings have worked for me. While I'm using Vagrant and PhpStorm has an extra Vagrant checkbox (which has not worked for me):

PhpStorm Settings

In your PHP command line programs you need to specify often arguments. That can be done also:

PhpStorm Settings
Tags

Eclipse mit Plugins zur PHP IDE rüsten

Submitted by Peter Majmesku on Mon, 08/29/2016 - 19:20

Hallo zusammen,
da ihr viele meist Remote arbeitet und da auch eine PHP IDE nützlich ist, ist Eclipse nicht schlecht. Im Gegensatz zu Netbeans oder PhpStorm lädt Eclipse mit dem Remote System Explorer Plugin die Dateien nicht herunter. Zudem sind Calltips für PHP Befehle und das Springen zu Funktionsdeklarationen möglich (Remote sobald die Datei mit den Deklarationen einmal geöffnet wurde). Man springt z.B. mit STRG + Klick mit linker Maustaste auf Funktionsaufruf im Code. 

Das Cacheing der Deklarationen klappt mit dem letzten Punkt in der unten aufgeführten Anleitung. Probierts mal aus. Ist eine gute OpenSource IDE für PHP Entwicklung, gerade wenn ihr Remote arbeitet, nicht einzelne Projekte anlegen und den Code herunterladen wollt.

http://www.patrickjwaters.com/blog/2011-07-24/how-setup-eclipse-php-pdt-remote-system-explorer-theme-manager-and-drupal-plugins/35?page=1

Beim Umsetzen "das Ganze" Tutorial durchgehen. ;)

Tags

How to use PHPUnit fixtures in your Yii2 development

Submitted by Peter Majmesku on Mon, 08/29/2016 - 19:04

Often you need to create extra database data to run your PHPUnit test, when you're creating your application with Yii2 framework. The best is, that you're doing this work only once and save this with your test, so that you can re-use this test data on every test run. This data is called "fixtures". I want to introduce you my approach of using fixtures with CSV files, which are containing the data which will be written into your database, as you're fireing your PHPUnit test. For programming and running PHPUnit tests I prefer PhpStorm. But you can test your code also with PHPUnit by command line of some configuration in Netbeans or Eclipse with PHP configuration.

PhpMyAdmin and MySQL workbench are allowing you to export CSV files with your database data and the column-names in the first line of the export. The contents of the first line are the names of your properties, which are defined in your Yii model. Ok, let's say you have such exports, you need to have your PHPUnit test.

use app\tests\fixtures\ModelFixtureDataCreator;
class 
MyTest extends PHPUnit_Framework_TestCase
{
    protected function 
setUp()
    {
        require_once 
'yii_booter_for_tests.php';
        
$test_classname get_class($this);
        
ModelFixtureDataCreator::create($test_classname);
    }
    public function 
test()
    {
        
MyController::export(1);
    }
 

At the beginning you see, that I use the ModelFixtureDataCreator(); class for creating my fixtures for the PHPUnit test. The folder structure in my project is the same as in the namespace. I'll explain you the ModelFixtureDataCreator(); class later in detail. The first method in my test is setUp(); which works like a constructor in PHPUnit. I require a file which is booting Yii2 for me, so that I can use it in my unit tests.

if(is_numeric(strpos(getcwd(), '/tests'))){
    require_once 
getcwd() . '/../yii_boot_console.inc.php';
} else {
    require_once 
getcwd() . '/yii_boot_console.inc.php';

This code is very easy, it handles just the paths for me, so that I don't have to mind from which folder I start my tests. This file is just booting Yii's console application. It's just the file you use to run your Gii application from the command line.

I've modified it a bit for my needs (you're free to modify yours as you wish):

<?php defined('YII_DEBUG') or define('YII_DEBUG'true);
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN'fopen('php://stdin''r'));
defined('STDOUT') or define('STDOUT'fopen('php://stdout''w'));
require_once(
__DIR__ '/vendor/autoload.php');
require_once(
__DIR__ '/vendor/yiisoft/yii2/Yii.php');
use 
app\controllers\CmPlatformDeterminationClass;
defined('ENVIRONMENT') or define('ENVIRONMENT''dev');
if(!isset(
$argv)){
    
$argv false;
}
// the path to your config file.
$config = require_once('the/path/to/your/config/file.php');
new 
yii\console\Application($config); 

Then I determine the name of my test class and pass it finally to my ModelFixtureDataCreator(); class, where I call the create-method statically:

<?php $test_classname get_class($this);
ModelFixtureDataCreator::create($test_classname); 

The magic from the ModelFixtureDataCreator(); class is, that it uses "convention over configuration". Once you have the following folder structure the class is able to create all fixture data in relation to your test.

- your yii2 application folder
-- tests folder
--- fixtures folder
---- folder exactly named like your test without the file extension (f.e. ExportControllerTest)
----- the csv files exactly named like your models (f.e. MyModel.csv)

 Downwards you can see the code from ModelFixtureDataCreator(); class with it's comments.

<?php amespace app\tests\fixtures;

use 
yii\base\Exception;


/**
 * Class ModelFixtureDataCreator
 * @package app\tests\fixtures
 */
class ModelFixtureDataCreator {

    
/**
     * Reads the CSV files from the test related fixture folder, instantiates
     * the model by the CSV filename without the file-extension and passes
     * it to the writeFixtureDataForModelIntoDb(); method with the modelname
     * and the test classname.
     *
     * @param $test_classname
     */
    
public static function create($test_classname){
        
$path__fixture_folder 'fixtures/' $test_classname;

        
$handle__fixture_folder opendir($path__fixture_folder);

        while ((
$file readdir($handle__fixture_folder)) !== false){
            if(
is_file($path__fixture_folder '/' $file)){

                
$arr__pathinfo pathinfo($file);
                
$str__modelname str_replace('.' $arr__pathinfo['extension'],
                                                                    
''$file);

                
$str__modelpath 'app\\models\\' $str__modelname;

                
$obj__model_instance = new $str__modelpath;
                
self::writeFixtureDataForModelIntoDb($obj__model_instance,
                                            
$str__modelname$test_classname);
            }

        }

    }

    
/**
     * Uses the first line from the CSV file as the model properties. Then
     * the method treats the next lines as values and writes them to the model
     * related database table.
     *
     * @param $obj__model_instance
     * @param $str__modelname
     * @param $test_classname
     */
    
private static function writeFixtureDataForModelIntoDb($obj__model_instance,
                                            
$str__modelname$test_classname){

        
$fp fopen("fixtures/$test_classname/$str__modelname'.csv''r');

        
$first_row_runed false;
        
$properties = array();
        while ((
$data fgetcsv($fp1000",")) !== FALSE) {
            if(
$first_row_runed === false){
                
$properties $data;
            } else {
                
$sub_counter 0;
                foreach(
$data as $d){
                    
$obj__model_instance->$properties[$sub_counter] = $d;
                    
$sub_counter++;
                }
                
$obj__model_instance->save();
            }

            
$first_row_runed true;
        }

    }


The class is currently only writing fixture data into your database. By the tearDown(); method from your PHPUnit test you're able to implement logic, which is deleting the fixture data after your test has been runned.

Do you have any ideas for this approach? Share them!

Subscribe to PHP