I have several hundreds of horribly indented PHP files with mixed tabs and spaces (and even mixed line endings, I suppose) I would like to fix them with php-cs-fixer v2+.
I have configured php-cs-fixer to my needs, and the code is scrubbed accordingly - except the indentation. I have tried a minimal configuration, like shown bellow, to pin down the problem. But I cannot get the indentation fixer straight:
return PhpCsFixer\Config::create()
->setRules([
'#PSR2' => true,
'indentation_type' => true,
'braces' => ['position_after_functions_and_oop_constructs' => 'same'],
])
->setIndent("\t")
->setLineEnding("\r\n")
Currently, I run this on my Windows box using the following command (here for a single file):
php-cs-fixer.bat fix new_user.php --config /full/windowspath/to/php_cs.dist
Just in case, the generated php_cs.cache (which contains the actually applied rules in JSON) file looks like this:
{
"php": "5.6.31",
"version": "2.6.0:v2.6.0#5642a36a60c11cdd01488d192541a89bb44a4abf",
"rules": {
"blank_line_after_namespace": true,
"braces": {
"position_after_functions_and_oop_constructs": "same"
},
"class_definition": true,
"elseif": true,
"function_declaration": true,
"indentation_type": true,
"line_ending": true,
"lowercase_constants": true,
"lowercase_keywords": true,
"method_argument_space": {
"ensure_fully_multiline": true
},
"no_break_comment": true,
"no_closing_tag": true,
"no_spaces_after_function_name": true,
"no_spaces_inside_parenthesis": true,
"no_trailing_whitespace": true,
"no_trailing_whitespace_in_comment": true,
"single_blank_line_at_eof": true,
"single_class_element_per_statement": {
"elements": ["property"]
},
"single_import_per_statement": true,
"single_line_after_imports": true,
"switch_case_semicolon_to_colon": true,
"switch_case_space": true,
"visibility_required": true,
"encoding": true,
"full_opening_tag": true
},
"hashes": {
"new_students.org_.php": -151826318
}
}
And here is some badly indented sample file content.
<?php
session_start();
include 'connect.php';
include 'functions.php';
$test= "abc";
$additional_studs = "";
if (date('m') == 12 and $term='SP') {
$yr_suffix = date('y') + 1;
} else {
$yr_suffix = date('y');
}
function dup_stud($id, $conn)
{//...
}
$i = 0;
I am most annoyed be lines like $test="abc"; & include 'connect.php'; with one or more leading tabs/spaces that do not get properly indented.
I am open to alternative approaches. Others must have faced formatting issues like this before.
I have also tried NetBeans, which happens to format the source beautifully, but it is tedious to open each file manually and apply the source formatting via shortcut.
You should use braces fixer to force indentation.
The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
indentation_type simply enforces consistency.
But since both the fixers are already included in #PSR2 so the code should be fixed correctly.
See the relevant sections in the README.
Using your code php-cs-fixer 2.6 produces the following code
<?php
$test= "abc";
$additional_studs = "";
if (date('m') == 12 and $term='SP') {
$yr_suffix = date('y') + 1;
} else {
$yr_suffix = date('y');
}
function dup_stud($id, $conn)
{//...
}
$i = 0;
where the indentation is only partly fixed.
I reduced it to the code below
<?php
echo "a";
echo "b";
echo "c";
It looks like a bug in php-cs-fixer.
I will answer my own question based on the findings that led me to a resolution.
While the formatting basically worked, the catch for me was the indentation. If there were some leading spaces or tabs, certain lines kept sticking out after the fix.
Since neither php-cs-fixer nor phpcbf was able to fix the indentation properly I took desperate measures and trimmed every leading whitespace from each line as preparatory step with sed in a script like this:
sed "s/^[ \t]*//" -i test.php
Then I processed some prepped files again with php-cs-fixer and phpcbf to find out which one does a better job formatting the files according to PSR-2. It's shameful, but both fixers failed again - now showing some different shortcomings (i.e. bugs). To cut a long story short, I finally learnt that coupling the two tools leads to properly formatted code files. What a mess.
So, after sed, I run phpcbf
phpcbf --standard="PSR2" test.php
followed by
php-cs-fixer fix test.php --rules=#PSR2
And all the sudden I have beautifully PSR-2 formatted PHP files. Not the most efficient way, but it does the job.
Some additional comments:
If you would like to apply additional fixer rules, I would suggest to do this in a 4th step using a different, more complete php_cs configuration from a PSR-2 baseline formatting (because, you know, there are more fixer issues..).
I suggest to use 4 spaces as indent, as required by PSR-2. According to my experience things get even more complicated if you insist to have tabs.
The described procedure wouldn't be necessary if php-cs-fixer and phpcbf would not have so many issues. I will report them one after another, and hopefully, in the future the same can be achieved in one go.
About alternative options. I also had a problem with automatic code formatting in Visual Studio Code. I tried some formatters but only phpfmt solved my problem with indentation and putting braces in the right place. It also has many customization options but I didn't test them, since they weren't needed.
OP says
I am open to alternative approaches. Others must have faced formatting issues like this before.
Our PHP Formatter will indent files nicely. See OP's "badly indented" sample processed by the PHP Formatter:
C:\>DMSFormat PHP~v7 \temp\test.php
PHP~v7 PrettyPrinter Version 1.3.17
Copyright (C) 2004-2016 Semantic Designs, Inc; All Rights Reserved; SD Confidential
Powered by DMS (R) Software Reengineering Toolkit
DMS_PHP~v7_INPUT_ENCODING=ISO-8859-1
DMS_PHP~v7_OUTPUT_ENCODING=ISO-8859-1
Parsing \temp\test.php [encoding ISO-8859-1 +CRLF +LF +CR +NEL +1 /^I]
<?php
include 'connect.php';
include 'functions.php';
$test="abc";
$additional_studs="";
if (date('m') == 12 and $term='SP') {
$yr_suffix=date('y')+1;
}
else {
$yr_suffix=date('y');
}
function dup_stud($id,$conn) { //...
}
$i=0;
(I had to add
<?php
to the start of the file to make it legal.)
This example was run from a file to the console. You can also do one file to one file, or run an entire list of files using a project file [this is probably what OP wants].
The PHP formatter uses a real PHP parser to process the source text and build an abstract syntax tree, and a special prettyprinter to print the AST back to nicely formatted text. It can't screw up the file.
Related
I read somewhere that php parses the whole .php file every time it is executed. Some solution was proposed in there (that was not opcache), but I lost the website and I couldn't find it.
Now I have an enormous php website that has many long functions that are often used alone, and it's required that the execution be fast.
To avoid having php parsing all the other functions that won't be used, I was thinking of making a modular design in which the functions, stored in independent php files, will only be included if they will be actually used. But I haven't been able to confirm that php will not parse an include inside of a function or inside of a conditional statement unless it is required. Does php parse those includes?
Example:
<?php
$func_to_execute = $_GET['func'];
$parameter = $_GET['parameter'];
switch($func_to_execute)
{
case 'a':
include 'func_a.php';
$output = func_a($parameter);
break;
case 'b':
include 'func_b.php';
$output = func_b($parameter);
break;
case 'c':
include 'func_c.php';
$output = func_c($parameter);
break;
};
echo $output;
?>
In this example, I would like php to parse only the func_a if I am requesting a, only the func_b if I am requesting b, etcetera. There are in practice more than just 3 functions, and each is a very long algorythm with also very long strings and arrays.
As an alternative to includes I was thinking of making independent php files and execute them and retrieve their output only if they are required, with a shell_exec. But that would take other complexities, like formatting the parameters (I don't have idea of how I would pass a very long string with special characters, or a JSON, as a parameter in the shell) and calling the function to execute in the shell. Would those complexities make it slower than just letting php parse the whole file?
I know about the opcache function. Would it be enough even if all the ops of all the functions will be tested each time?
Are there other ways to make a PHP website modular, and not having php parsing the whole of php files everytime?
Thank you.
since php uses many optimizations and caching apcu i.e. you dont need to care about this
include wont be parsed at load time.. its more like file_get_contents and execute in same context - and these will be optimized by internal php cache
http://php.net/manual/en/intro.apc.php
I made a benchmarking experiment and it seems that php truly does not parse conditional includes. I made the test using the example script mentioned, and defining each as:
func_a: it only declares that the value of the variable $x is the sentence 'war and peace'.
$x = 'war and peace';
func_b: it only declares that the value of the variable $x is the whole text of the novel war and peace, which is approximately 3.2 MB long (the whole text was pasted in the php file). This would be a very long file to parse.
$x = 'War and Peace, by Leo Tolstoy...(the whole novel...)...';
func_c: it contained incorrect syntax, that should immediately launch an error message from php. This was made to guarantee that php was not actually parsing what was not included.
I measured the execution time from another php script with the function shell_exec(). The results were (in seconds):
func_a ≈ 0.122
func_b ≈ 0.152
func_c ≈ 0.119
Therefore I conclude that:
- Includes in a switch statement are not parsed unless they are actually required.
- A syntax mistake in an include (inside a switch statement) will not launch any error if it is not actually required, because it is not parsed.
- Anyway, the difference on the time of the process is very little (about 0.03 extra second for an extra text of 3.3 MB; or crudely said, 0.01 extra second per 1 MB of text to parse). However this might be important to consider if there are many users requesting the website at the same time, and therefore it might be useful to divide in modules (includes) if the script is actually that big. Also the fact that a wrongly written include that was not required be not parsed helps to not launch errors when they aren't relevant.
It seems then for me a good manner to design a modular application in PHP where the modules be extremely big.
Suddenly, an application isn't any longer able to output ZIP files. An inspection revealed the cause: The first character of the ZIP is a blank, which breaks the ZIP format spec.
To track down this problem, I enabled CStatementTracer, which prints each line of executed code to a log file. Didn't help. [Remark: declare(ticks=1); doesn't seem to trap each line of executed code]
I then set an output handler like so:
function callback( $buffer ) {
$deb = print_r( debug_backtrace(), TRUE );
file_put_contents( './statementTrager.log', $deb );
return $buffer;
}
ob_start("callback", 1 );
Unfortunately, this handler isn't called at all.
Q: Does a generic / canonical solution exists, which identifies the file / line of PHP-code, which emits the first character.
A solution, that finds the loc whatever other code gets executed.
Remarks:
Not even a single PHP file is closed using ?>
Meanwhile I found the suspicious like of code: A blank in front of a starting
Still, I'd like to get hints regarding a programmatic solution. Preferrably a solution written in pure PHP.
https://linux.die.net/man/1/strace is probably the most reliable tool to find out where the output comes from. Assuming you are on Linux. There must be similar tools for other platforms.
Although it will not give you the line of the php code, you can analyse the context of system calls made before and after the offensive character was sent. Usually it is enough to identify where the problem originates.
It is quite time consuming process though. Should be used as the last resort.
This question already has answers here:
making print_r use PHP_EOL
(5 answers)
Closed 6 years ago.
I've been coding in PHP for a long time (15+ years now), and I usually do so on a Windows OS, though most of the time it's for execution on Linux servers. Over the years I've run up against an annoyance that, while not important, has proved to be a bit irritating, and I've gotten to the point where I want to see if I can address it somehow. Here's the problem:
When coding, I often find it useful to output the contents of an array to a text file so that I can view it's contents. For example:
$fileArray = file('path/to/file');
$faString = print_r($fileArray, true);
$save = file_put_contents('fileArray.txt', $faString);
Now when I open the file fileArray.txt in Notepad, the contents of the file are all displayed on a single line, rather than the nice, pretty structure seen if the file were opened in Wordpad. This is because, regardless of OS, PHP's print_r function uses \n for newlines, rather than \r\n. I can certainly perform such replacement myself by simply adding just one line of code to make the necessary replacements, ans therein lies the problem. That one, single line of extra code translates back through my years into literally hundreds of extra steps that should not be necessary. I'm a lazy coder, and this has become unacceptable.
Currently, on my dev machine, I've got a different sort of work-around in place (shown below), but this has it's own set of problems, so I'd like to find a way to "coerce" PHP into putting in the "proper" newline characters without all that extra code. I doubt that this is likely to be possible, but I'll never find out if I never ask, so...
Anyway, my current work-around goes like this. I have, in my PHP include path, a file (print_w.php) which includes the following code:
<?php
function print_w($in, $saveToString = false) {
$out = print_r($in, true);
$out = str_replace("\n", "\r\n", $out);
switch ($saveToString) {
case true: return $out;
default: echo $out;
}
}
?>
I also have auto_prepend_file set to this same file in php.ini, so that it automatically includes it every time PHP executes a script on my dev machine. I then use the function print_w instead of print_r while testing my scripts. This works well, so long as when I upload a script to a remote server I make sure that all references to the function print_w are removed or commented out. If I miss one, I (of course) get a fatal error, which can prove more frustrating than the original problem, but I make it a point to carefully proofread my code prior to uploading, so it's not often an issue.
So after all that rambling, my question is, Is there a way to change the behavior of print_r (or similar PHP functions) to use Windows newlines, rather than Linux newlines on a Windows machine?
Thanks for your time.
Ok, after further research, I've found a better work-around that suite my needs, and eliminates the need to call a custom function instead of print_r. This new work-around goes like this:
I still have to have an included file (I've kept the same name so as not to have to mess with php.ini), and php.ini still has the auto_prepend_file setting in place, but the code in print_w.php is changes a bit:
<?php
rename_function('print_r', 'print_rw');
function print_r($in, $saveToString = false) {
$out = print_rw($in, true);
$out = str_replace("\n", "\r\n", $out);
switch ($saveToString) {
case true: return $out;
default: echo $out;
}
}
?>
This effectively alters the behavior of the print_r function on my local machine, without my having to call custom functions, and having to make sure that all references to that custom function are neutralized. By using PHP's rename_function I was able to effectively rewrite how print_r behaves, making it possible to address my problem.
I'm fetching output from the exec() function and I would like to have some syntax highlighting in the results.
Raw output
Current output is raw:
* [35mmanu[m/etc/init.d/mast: line 105: /var/log/mast/mast-all.log: Permission denied
Text such as [35m are color highlighting in shell context.
Goal
I want to do it in HTML, I already have a colored shell script.
The question A library to convert ANSI escapes (terminal formatting/color codes) to HTML has an answer to this question.
Solution
aha is a Ansi to HTML Adapter written in C. It's available in an Ubuntu
package and on github theZiz/aha.
My code is then simply:
exec("$command | aha", $output, $exitCode);
foreach($output as $k => $line) {
if ($line == '1') { continue; }
echo "$line";
}
Description
aha takes SGR-colored Input and prints W3C conform HTML-Code.
aha reads the Input from a file or stdin and writes HTML-Code to stdout.
There is some nice options:
--black , -b: Black Background and white "standard color"
--word-wrap , -w: Wrap long lines in the html file. This works with CSS3 supporting browsers as well as many older ones.
--no-header , -n: Don't include header into generated HTML, useful for inclusion in full HTML files.
You can also do it in pure PHP thanks to symfony. using ansi-to-html library.
It has no dependency, so it is not required to use symfony.
You can install it manually by coping SensioLabs\AnsiConverter\AnsiToHtmlConverte.phpin your source folder or using composer
composer require sensiolabs/ansi-to-html
After that just a
require_once __DIR__.'/vendor/autoload.php';
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
$converter = new AnsiToHtmlConverter();
$html = $converter->convert($ansi);
I am trying to set up omni completion for PHP in vim 7.3 with ctags 5.9~svn20110310 on Ubuntu 12.04.1 (LTS) but I am running into a very strange issue where completion provides radically different predictions for instances of the same class.
I have the following two files:
// Foo.php
class Foo {
public function do_stuff() {
echo 'Working...';
}
}
// index.php
require 'Foo.php';
$f = new Foo();
$f->[cursor position 1]
$g = new Foo();
$g->[cursor position 2]
When the cursor is in position 1 and I press CTRL+X CTRL+O it comples the line with do_stuff( as we would expect. But when I press CTRL+X CTRL+O in the second position I get a list of predictions that starts with key, next, rewind. What am I doing wrong?
Edit: With regard to your specific issue, if you have an old version of phpcomplete.vim, it's possible that you can only properly complete off a variable either by marking it with a special phpdoc tag (see this question) or by regenerating your tags file after declaring the variable.
In all probability, you are doing nothing wrong; the PHP support in ctags is extremely basic and not very rigorous, which unfortunately means the Vim support is lacking, too. A quick look at the ctags module illustrates the problem:
ctags/php.c
That's it. Just a couple of relatively basic regular expressions. That parser stuff at the bottom is not used any longer, and tragically hasn't been for a very long time.
Compounding the issue is the fact that the standard omnicomplete function for PHP in Vim is hackish at best; suffice it to say that it involves switching between all the open windows as part of its completion process (a practise explicitly condemned by Vim documentation). Take a look for yourself:
phpcomplete.vim/autoload/phpcomplete.vim
I have struggled with terrible PHP completion in Vim for a long time now and have determined that nothing short of a complete overhaul will produce a satisfactory result. I've joined the ctags dev mailing list, and I plan to improve the PHP support there before moving on to making Vim's omnicompletion thereof work as properly as it can in an interpreted language. For now, unfortunately, the solution is to wait until the support is better, or fix it yourself.