Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The main problem with PHP 5 was never the lack of features, but things like "there is no way I can get an error code from fopen()", and things like that. Can I now get EEXISTS and such out of fopen()? Looking at the docs it still just returns false and emits an uncatchable E_WARNING that can't be examined programmatically.

Having to write a bit more code because it doesn't have some feature is not ideal, but whatever. Not able to correctly race-free implement file access is a much bigger issue. I have some other gripes (which may or may not be fixed), but this was and remains my main one.



> Can I now get EEXISTS and such out of fopen()? Looking at the docs it still just returns false and emits an uncatchable E_WARNING that can't be examined programmatically.

Sure, the most straightforward way is to first use the "x" flag for write-only, and "x+" for read/write access.

  $fp = fopen('file.txt', 'x+');
From the PHP docs:

"If the file already exists, the fopen() call will fail by returning false and generating an error of level E_WARNING. If the file does not exist, attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for the underlying open(2) system call."

And finally to convert E_WARNING errors to exceptions, use you can use set_error_handler() to throw an exception (and even write business logic to filter them): https://www.php.net/manual/en/language.exceptions.php

They're not unhandleable.

But if you want a single function that throws a specific exception when a file exists, specific to the behavior you want, I got you:

  <?php
  declare(strict_types=1);
  
  namespace Argp242;
  use function fopen as php_fopen;
  
  function fopen(
      string $filename,
      string $mode,
      bool $use_include_path = false,
      $context = null
  ) {
    if (file_exists($filename)) {
      throw new \RuntimeException('E_EXISTS');
    }
    return php_fopen($filename, $mode, $use_include_path, $context);
  }

To run this code from outside a namespace, simply:

  try {
    $fp = Argp242\fopen($filename, $mode);
  } catch (RuntimeException $ex) {
    // Handle $ex
  }
Throw it in a library somewhere, having written it once, and now you can just use that whenever you want that behavior in your PHP code.


There are tons of reasons an fopen() call can fail and sometimes you really want to know why; these pokemon exceptions don't give me that. And your code is racy as files can be created in-between the file_exists() and fopen() calls. Yes, this happens, and in some cases it's a security problem (which is why you need to use mkstemp and not mktemp in C).


The core of PHP is very close to the libc function it offers, and so you will do that in a similar way that you do that in C : call error_get_last(), which gives you the details of the last error.


this code will run into problems with race conditions if the file is created after file_exists is called but before fopen is called


If you expect PHP to ever win a race against another process, I don't know what to tell you.

Trying to make PHP work for a job it's not suited for and failing is not a reason to hate PHP. It's a reason to ask yourself "why".


This absolutely will happen, and most likely the other process is also PHP. The most obvious example is caching anything to a file. I don't hate PHP though, I just don't feel the need to convince myself it couldn't be improved.


A lot of PHP usage is inherently multi-process; 100 users accessing your PHP app are essentially 100 processes, often with a few of them running in parallel. Doing any sort of file i/o really does require thinking about these sort of things, and it's something I've run into.

And this is not exactly a huge ask, or a very obscure function call. You can kind of work around all of this if you're careful, but it's not exactly brilliant is it? Literally every other mainstream language can do this. This is really a "only in PHP" type of thing.

Just say "yeah, this is still a downside of PHP; hopefully it will get improve one day!" Fine. Fair enough. No language is perfect and I'm not here to bash PHP. I consider this to be a major pain point, but other people do other things and maybe it's less of an issue for them. Also fine. What I don't get are these contortionist tricks – I already knew how this would go when I posted it: first there would be the "but you can do this", and then when mentioned that's not really sufficient the "yes but you don't need that anyway".


We ran several quite popular (millions of monthly users) PHP websites for almost two decades and never ran into any issues of this nature, including a lot of local on-server file I/O. I don't recall doing anything specific or particularly advanced to deal with these issues, just some basic encapsulation was enough and it worked seamlessly.


If it's a pain point, write an RFC. Make the language better. Don't just shrug and say "it sucks"


Eh, you can constructively critique things without spending tons of effort on fixing it.

What a painful and unpleasant conversation this is turning out to be.


> What a painful and unpleasant conversation this is turning out to be.

Finally, something we both agree on


If you change the behavior of fopen() by installing an error handler that throws an exception, won't that mess up library code that doesn't expect that behavior? Seems hacky and error prone to me. (Also the race condition others already mentioned!)


The old core of PHP is very close to the libc function it offers, and so you will do that in a similar way that you do that in C : call error_get_last(), which gives you the details of the last error.


How do I do "errno == EXISTS" like in C? This is bit I need. The issue for this was closed as essentially as wontfix: https://bugs.php.net/bug.php?id=49396


FWIW as a minor correction, the issue was not closed as wontfix, but for procedural reasons. This would require someone to submit an RFC for it.


> "there is no way I can get an error code from fopen()",

You can, it's via `error_get_last()`. We've had that for 17 years..


How do I test if this is EEXISTS and not, say, a full disk? Or too many symlinks? Or something else? Because last time I check it's not really possible, but maybe this has changed?


The problem is that you're trying to use PHP as if it's a different language.

If I have to deal with files, I'll create a class and I'll combine functions file_exists, is_file, is_writable, file_get_contents, file_put_contents (and new fsync) in conjunction with stream_context_create. I can even get the errors that OS spits out and test for what exactly went wrong, but you obviously ignored that and gave up. I can tell you used the language sporadically, not professionally.

You're taking a small, localized issue that arose, while your job as programmer is to get around idiosyncrasies in programming languages with the focus on achieving the end goal.

I've a list of terrible things that PHP does but I have one for JS/TypeScript/Go/Rust/<you name it>.

My job is to know these and achieve goals, not to criticize languages because file handling in it is not the same as in C++ and language didn't fire up 50 AWS instances to spam X.com with messages in hopes that I somehow notice them.


> I can even get the errors that OS spits out and test for what exactly went wrong

Coolio, so how do you do this?

I spent several years full-time working with PHP. I ran in to this several times. It's not a small issue. And my entire point is I can't work around it, because as near as I can tell, it just doesn't offer me the required primitives. That was certainly the case when I last looked at this, but that was a long time ago so maybe things changed?


Agreed and just to be clear, fopen() returns false if an error happens. If you don't check your return values, it's not on php, you're just not very good at your job.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: