rfc:println

This is an old revision of the document!


PHP RFC: println(string $data = ''): int

Introduction

Printing a string followed by a newline to stdout is a commonly performed operation in many applications. Many programming languages provide a helper function to do this specifically, for readability and convenience. The choice of end of line may differ, but many recent programming languages will unconditionally use the unix newline, to avoid unexpected differences in behavior between platforms.

  1. Java has System.out.println('hello world')
  2. Python print('hello world') prints a newline by default, and a named argument can override that.
  3. C has puts(“hello world”)
  4. Golang has fmt.Println(“hello world”)
  5. Rust has println!(“hello world”)
  6. And so on.

However, PHP currently does not yet have a standalone helper method to do this.

Proposal

Add a global function println(string $data = “”): int to PHP.

The behavior is equivalent to the following polyfill but expected to be more efficient due to avoiding concatenation.

Similarly to https://www.php.net/manual/en/function.printf.php#refsect1-function.printf-returnvalues - this returns the number of bytes that were successfully output to stdout (to be consistent with printf). In the typical case where there was no output error, this returns strlen($data) + 1. (E.g. printf and println may fail if php's standard output was redirected to a file on a disk that filled up)

/**
 * Prints $data followed by a unix newline
 * @return int the number of bytes that were successfully printed to stdout.
 */
function println(string $data = ''): int {
    return printf("%s\n", $data);
}
println("test");
println();  // moderately useful to not switch to echo or pass the empty string to print a blank line
println("third line");
/*
Output:
test
 
third line
*/

Reasons to Add This

  1. This is useful for self-contained scripts and a useful helper function to have overall. E.g. phpt tests of php itself print multiple lines for the --EXPECT-- section, and var_dump can be overused even for known strings known not to have special characters or spaces because var_dump(some_function()) is a bit simpler to write than echo some_function() . “\n”;, but not as simple as println(some_function())
  2. Even if codebases add userland helper equivalents that do exactly this, If you are new to a codebase, or contribute to multiple codebases, it is inconvenient to use xyz_println, ABCUtils::println(), echo X, “\n”, etc., and remember if those different functions actually use the line endings you think they do.
    Additionally, the prefixing is much more verbose.
  3. In tutorials or language references that teach a developer how to use php functionality, it is often preferable to use functions that append a newline when multiple snippets would be evaluated together to keep examples simple.
    println(“Hello $name”); would be useful to have for introducing PHP to a new developer before echo “Hello $name\n”; (requires explaining control characters first) or var_dump(“Hello $name”); (that debug representation is rarely useful for string(11) “Hello world”)
    E.g. var_dump is frequently used instead of var_export, echo, or print in the manual even for printing strings with no control characters such as https://www.php.net/manual/en/function.json-encode.php#example-3972

The Unix Newline is always used

This deliberately always prints the unix newline (\n) instead of PHP_EOL, for the reasons mentioned in this section.

I would find it very unexpected if println were to behave differently based on the web server was running it, e.g. if you moved a website's backend from/to a Linux server to/from a Windows server, responses generated by println would suddenly be different. (Content-Length, hashes(e.g. sha256 sum) of output, etc.)

Additionally, https://www.php-fig.org/psr/psr-2/ recommends that all php source files contain unix line endings. If those files contain inline html/text snippets mixed with php+println(), or if they contained strings using <<<EOT heredoc, it would be inconsistent to have \r\n in the lines printed by println() and \n for heredoc and HTML when running on Windows.

The unix newline is same choice of line ending that var_dump, debug_zval_dump, and var_export use for dumping output. Otherwise, println(“myArray=” . var_export($myArray, true)); would be a mix of multiple line ending choices.

PHP's interactive shell (php -a) also prints a single newline character if the output does not end in a newline.

// ext/readline/readline_cli.c
  	if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') {
  		php_write("\n", 1);
  	}

Many new languages have elected to always use only the unix newlines, e.g. https://golang.org/pkg/fmt/#Println and https://doc.rust-lang.org/std/macro.println.html

Overall, editors do a much better job of detecting newline choices and displaying different newline choices than they did decades ago.

My opinion is that this anything generating files targeting a specific OS's line endings should continue to use PHP_EOL or continue to base the newline choice on the OS of the user requesting the output.

Type checking

Type checking is done the same way as other ordinary user-defined or internal global functions. println(1); or println(true) would have the argument coerced to a string when strict_types is disabled (the default), but would be a thrown Error with strict_types=1 (like printf would for the format string).

Depending on the application, this may be useful as a runtime assertion or in making it clear to the reader that the argument is expected to be a string.

println((string) $value); should be used when strict_types=1 but you are uncertain of the type.

(or other alternatives such as echo $arg, “\n”, echo “$arg\n”;, or printf(“%s\n”, $arg))

Backward Incompatible Changes

Declaring a function named println() in the global namespace would become a duplicate function error.

Proposed PHP Version(s)

PHP 8.1

Unaffected PHP Functionality

Other printing functions or statements are unaffected.

println is deliberately NOT a keyword (e.g. functions named println can continue to be declared outside of the global namespace).

Discussion

This could go in a Composer/PECL package instead

https://externals.io/message/104545#104548

<?php
 
function println(string $x): void {
    echo $x, PHP_EOL;
}

I hereby grant a public domain license to the above code and wish you godspeed bundling it into a composer package to be enjoyed by users of every active version of PHP.

-Sara

In practice, I haven't seen many widely used composer packages that contain a small number of functions - PHP tends to be a batteries included language. Additionally, this increases startup times in situations where opcache and/or opcache preloading isn't feasible to set up (especially due to the lack of function autoloading).

A new contributor to a project might not even be aware the helper method is available in a dependency, or of the choice of newline used in a helper method.

If multiple composer packages were published and declared println (in the global namespace) with different behaviors, that would lead to confusion and bugs, which could be avoided by declaring println in PHP itself.

This may not be commonly used for HTML

https://externals.io/message/104545#104560

3. Add a new method, perhaps “echoln”, “println”, “say” or similar, that outputs a newline by default

Of the suggestions put forward, this is the only one I can see having any chance of succeeding.

However, I think the big reason this doesn't already exist is one that's been touched on by other responses: PHP started as, and is still primarily regarded as, a language for building websites. In that context, newline characters are generally considered “insignificant whitespace”; the closest equivalent would be appending '<br>' or '<br />' (depending on the dialect of HTML in use), but you're as likely to want “<p>$foo</p>”, or “<li>$foo</li>”, etc - and that's before we get into the tricky topic of escaping.

Even in CLI scripts, as soon as you're building anything intended for reuse, you're likely to write a function like log_string() which adds information like timestamp, category, severity. The use cases for a new function / keyword may therefore be rather limited.

Regards,

-- Rowan Collins [IMSoP]

Some CLI scripts would use a specialized helper, but not all of them, and a helper may be used in some places but not others. Some CLI scripts are distributed without any external dependencies and the addition of println would simplify them and make them easier to read.

Even when used as a web server, PHP would also serve resources with non-HTML content types such as Content-Type: text/plain (e.g. health checks). Within HTML, there are elements such as <pre>, <textarea>, <script>, etc. where newlines are needed and used instead of <br>

Choice of println for the name

Naming was discussed in a different user's proposal to add a “puts” function that was never brought to a vote. https://github.com/php/php-src/pull/3918#issuecomment-502241701

nikic commented on 14 Jun 2019

Speaking of ... I think it would make a lot more sense to call this function println rather than puts. The fact that puts() is like print but with a newline will not be obvious to anyone without a C background. (Or even to someone with a C background for that matter, I haven't ever used this function and wouldn't know that it adds a newline.)

As stated in that comment, new languages such as golang/rust (and older languages like java(different semantics)) have gone with println instead. Additionally, the fact that puts would append newlines and fputs does not (exactly the same as it does in C) is something that I'd expect to be a source of confusion if the name puts was used.

(e.g. changing puts(“test”) to fputs(STDERR, “test”) would unexpectedly not print a terminating newline to STDERR (fputs is an alias of fwrite))

Proposed Voting Choices

Yes/No, requiring 2/3 majority.

References

https://externals.io/message/104545 “print with newline”

rfc/println.1615663791.txt.gz · Last modified: 2021/03/13 19:29 by tandre