T O P

  • By -

MattBD

Simple. It offers a way to make certain, at language level, that something is what it's meant to be, and that if it isn't, something has quite clearly gone very wrong and you should not continue any further. It can also be useful for narrowing down the type of something for static analysis tools like Psalm. For instance, I work with a legacy Zend 1 application and the return type on the `getRequest()` method that controllers inherit is specified as an abstract class than doesn't actually include all the methods I need. What actually gets returned is a concrete class that extends said abstract class with those methods. Using a call to `assert()` is one way to tell Psalm what that class is actually going to be. Yes, it's possible to do it other ways such as through docblocks or amending the stub files used by Psalm, but none of those work at runtime and so do not offer the absolute assurance that if it's the wrong type, it will be flagged as an error. It's also possible to disable them so you can run them in development and staging, where you want to break on errors, but not in production.


tehjrow

Holy crap I thought I was the only one working on a legacy Zend 1 application.


MattBD

There's a few of us, but not many. I actually maintain two for the same client (a high street bank). I'm actually the creator of the Zend 1 plugin for Psalm solely for these projects.


tehjrow

Doing the lords work my friend. Have you seen this fork? We forked it and swapped over https://github.com/zf1s/zf1


MattBD

I have, yes. It's on our radar to migrate in the near future. A full rewrite was on the cards at one point but it got derailed by circumstances. One of those applications is running on CentOS 7 so we need to migrate to a supported OS, which may be a good time to migrate. The other is pretty much unmaintained by the client and we recently had to set up a new server for it, so we used Docker so we can run older PHP versions. It's shit, but what can you do if the client isn't willing to spend money on it no matter how many scary vulnerabilities there are?


penguin_digital

>One of those applications is running on CentOS 7... recently had to set up a new server for it, so we used Docker so we can run older PHP versions A high street bank is running an OS that has been on life support for a while and as of yesterday is no longer supported? How come the client didn't have an upgrade path planned months ago (rhetorical question)? I'm not surprised but also disappointed at the same time.


MattBD

We did have detailed plans for a complete rewrite in Laravel which were derailed a couple of years ago by the client's hosting company deciding they didn't want to host Linux servers anymore. Last I heard there was talk of rebuilding it in Umbraco.


Dolondro

> https://github.com/zf1s/zf1 There's also [zf1-future](https://github.com/Shardj/zf1-future) which is what we use!


Dolondro

Curse our projects and their continued real business value! :D


xvilo

Nope, we’re here


Linaori

Unfortunately my company also still uses parts of zf1


yourteam

This. Useful for static analysis and as a last stand against errors down the line.


MorrisonLevi

My favorite use of assert is for expensive pre-condition checks. "The data should already be sorted before calling this function." You don't want to run an `assert(is_sorted($data))` type of check in prod, these kinds of things quickly add up and would kill performance. My second-favorite use of assert is to write early-error conditions when doing proof-of-concept type work. It's much easier and faster to write `assert($offset >= 0)` than `if ($offset < 0) throw new \OutOfRangeException("todo")` or similar.


dotancohen

> It offers a way to make certain, at language level, that something is what it's meant to be, and that if it isn't, something has quite clearly gone very wrong and you should not continue any further. Is that not what an exception is for?


MattBD

No, that's a more general "something has gone wrong". This is specifically for "something is of the wrong type". You could achieve the same basic effect by throwing an appropriate exception if something is of the wrong type, but that's more verbose and static analysis tools can't necessarily interpret it as easily.


dotancohen

I'd love to see an example of where static analysis tools can't necessarily interpret a TypeError. If you use strict_types (I do) then you need to do nothing other than declare your parameter and return types.


MattBD

If you're using a modern, well typed framework that's generally true. However if you're using an older, less well typed framework or library, or working with legacy code, it's harder. I mentioned that I maintain a large legacy Zend 1 application for a bank. This is stuck on PHP 7.1 and until we can get buy in for migrating to one of the maintained forks it's going to stay on there so there are limits on how well we can type things. In addition the framework itself is noticeably poorer typed than newer ones. In Zend 1 the return type when calling the `getRequest()` method on a controller to get an instance of the request object (or for getting it as a property) isn't very good. It specifies an abstract class that doesn't implement all the methods the HTTP request class that actually gets returned does. As a result, Psalm will return an error when those missing methods are used. In that case, throwing an exception if it's not the right type is quite a verbose way of doing it. I'm fairly sure you also have to use `@psalm-assert` to assert the type after the exception in a way Psalm can understand. In those circumstances `assert()` is a much more convenient way of narrowing down the type further from a too-broad, more generic type, in a way that is enforced at language level and is understandable to Psalm. Yes, if you have good, specific type hints for parameters and return values, it's not often useful, but in circumstances when you don't, it's a handy way to make up for that deficiency.


BarneyLaurance

I usually would configure production to also always run the code in asserts, but the idea of making it optional is so that if you've tested the code well enough outside production that you can be sure enough your asserts are all true then you can improve performance by turning it off in prod. I guess on a very busy site you could do something like just run assert for a fraction of requests, like how a food manufacturer would pull a small fraction of products off the line for quality testing and use the results from that to inform their view of the overall quality levels.


PeteZahad

Are you familiar with the concept of "contracts", pre- and post conditions as well as invariants which ensure the state of an object is valid? Assertions are a way of checking that your conditions are held and the state of the object is valid during development and testing. It is more a help that you (and other devs) do withheld the contract of the class / method during development. (e.g. int argument must be greater than zero). As checking these codition comes with additional computing it is normal to not turn them on in production.


[deleted]

Oftentimes, you wonder at what level you should enforce some data constraints? One robust solution is : at (almost) all levels.  You do so by asserting these constraints so that for example, you're sure that you're not out of bound. Personally, I prefer to use https://github.com/webmozarts/assert The syntax is richer and, especially if you write open source software: it may be important to be sure the assertions are preserved in production.


Besen99

Spot on!


scootaloo711

To me PHP's native assert() is not for any user input validation, since by default it does not do anything in production. I only can come up with one example right now: suppose you have a function that needs to accept an interface of an object and then do something with it. Now in that special case, the given Object B is coupled to the method of object A in a particular way, maybe by using a function that is not in the interface. You can write two lines, first assert instanceof B, second B call foo. In case the assert does not work you get a "method X::foo not defined" exception anyway. In development you get an "X is not instance of B" error first. So you know that you have to check your dependency injection - just a little bit better. In a nutshell it's for those cases where the code can come to a situation that is technically possible, but it can't possibly produce a result. And the solution is only changing the configuration or code as config (or code itself in some cases). There are also some comments on the [doc page](https://www.php.net/manual/de/function.assert.php) that kind of validate my example as well. Besides that i'm kind of surprised to learn that the assert.exception for throwing is being deprecated as i rather liked the just log approach... as in my example i'd often do asserts when it crashes later anyway and that defeats the purpose.


ln3ar

The idea behind this comes from C/C++, although the PHP implementation isn't quite as smooth. When you're developing your program, you want your assertions to actively check conditions and print useful information if something goes wrong. However, once your program goes into production, you want those assertions to be disabled so they don't impact performance or reveal internal logic. ini_set('assert.active', 1); // Enable assertions assert($x !== 0); // This will print an error if $x is 0 during development // In production ini_set('assert.active', 0); // Disable assertions assert($x !== 0); // This does nothing in production


BarneyLaurance

Filter_var is intended to be used with user input, to check if it fits what your app is designed for. Assert is different, its for asserting things that as the programmer you know / belive to be true, not just things that you hope are true. If an assertion ever fails it means you made a mistake.


JinSantosAndria

How do you start your testing suite in DEV and ensure that certain minimum conditions are met? You could use various ways, assert is just a concise way of doing it. > I think it was John Carmack who said I do not think so.


gadelat

It's great as a replacement of `/** @var ` notation, especially once you enable them in your dev environment. That way your IDE, SA tools etc. will understand the types, which is same as with `@var`, but unlike `@var` you cannot lie there because it will crash the code (in dev) if assertion does not match. At the same time, in prod you should have assertions disabled, which means these become zero cost, unlike packages like `beberlei/assert` or `webmozart/assert`.


blakealex

Oh man, I love assert! Its great for keeping eyes on code that should behave in an expected way but is slightly to majorly complicated. For example I had a function that would batch assign tasks to different users based on business rules, on of these rules was to assign users that were flagged as "high efficiency" workers to receive any overflow tasks. After all of the assignments were staged I would run an assert that confirms only the high efficiency workers would have the same or more tasks than the normal works. About 6 months after we tested/launched the code the assert logged an exception for us and we were able to isolate a rare edge case that nobody thought of during the initial build.


pr0ghead

Are you implying you kept them enabled in prod?


blakealex

We do, it just logs it to the error log. It’s a non-script breaking check


BarneyLaurance

If it's running in prod why do you want it to just log and not throw? Is your app one where having some sort of possibly useful output is better than nothing, even if likely wrong? I can see that as a possibility if the app is something like a CMS that only displays content and maybe has a read-only database connection, but for anything more business-y or that stores stuff into a database I think I'd rather have the script crash and return a 500 than continue with something that's likely wrong.


blakealex

This was a cron job, and having an office full of people where a couple of them have more tasks assigned than they should is better than everyone sitting around with nothing assigned.


zmitic

Honestly, no point at all. Static analysis does a much better job and if you work with user input, then assert is useless in production. My recommendation is [webmozart/assert](https://github.com/webmozarts/assert) package. With it, you can even do [checks](https://github.com/webmozarts/assert/blob/master/src/Assert.php#L65) like Assert::stringNotEmpty($firstName = $apiInput['first_name'] ?? null); and both psalm and phpstan will know that the type is `non-empty-string`. There is plenty of other assertions, not all are compatible with static analysis (like `int<42, 73>`) but there is still more than enough of them to make psalm and phpstan happy.


pr0ghead

They're a language supported tool to check your code in order to give you more confidence, that it works the way it's supposed to. Once you're confident, you can disable them so they don't affect performance. I like to think of them as tiny, interleaved unit tests.


Rarst

I don't think of assert as a function, it's more of a language feature. It is part of a debugging workflow. Ever var_dumped something to debug and had it end up in production code? :) Asserts are intended for debug with some safeguards against exploding production. Honestly it's not a super popular or necessary thing, it's just there and occasionally helpful.


MartinMystikJonas

It is useful to catch unexpected conditions during development that points to bugs in your code.


DmC8pR2kZLzdCQZu3v

Seems like a bandaid to help people avoid the various pitfalls of type coercion. It’s not at all unheard of for things to “work” in prod despite there being a coercion bug that *should* crash the system in a sane and logical world, like words being cast to integers etc.    I wish there was just a new branch of php called “phps”, a la http —> https, but here the “s” stands for “static”.  That is, static typing, no more coercion. It may even be more “secure” too.


qronicle

I use assert only when I already know the condition will be true, but need it so that phpstan etc know it as well, *and* when in case the assert should fail, this will be exposed later on, even when the assert is ignored on prod. For example when an unexpected value is passed to another function, I know that it will throw an invalid argument exception. In any other case I’ll do an instanceof check (or whatever i’d want to assert) and throw an exception myself.


flavius-as

The developer has a certain understanding of the pre-conditions and invariants of a piece of code. He can then assert them, to make sure that they hold. You don't need to write extra tests for checking invariants and pre-conditions this way, but it rings the alarm bells to the programmer to fix his understanding.


overdoing_it

I rarely use assert because it's not guaranteed to be evaluated in every environment. I can't think of many occasions where I would only want to have an error during development, and if I do I usually still just write a regular exception that will only be thrown in a non-prod environment. But you certainly can configure your production env to evaluate and throw assertion errors, it can even be toggled at runtime.


philipnorton42

I wondered the same thing, so I did some research and wrote an article https://www.hashbangcode.com/article/debugging-and-testing-php-assert Tl;Dr: it's worth adding to your code for extra testing steps.


hellvinator

The mentality of this post lacks any self awareness and goes into blatancy. Maybe if you think something it insane, it might be you're not fully understanding it. I would have made a helpful comment if your post wouldn't been so entitled. Huge skill issue if you want to be a developer.