Closed Max13 closed 2 years ago
Hello 👋
We might implement a global setToStringFormat
for consistency with Carbon
date time class, but please keep in mind that it comes with some unexpected risks that are hard to anticipate.
For instance you might have sub-sub-dependency you're not even aware of that is casting CarbonInterval
and relies on the forHumans() output to make some parsing, generating content for mail or whatever. If you set a global format for all CarbonInterval
, it will apply to all casts to string in your own code base but also all the casts in your vendor code and you probably don't control 100% of that code, plus it might change when you update dependencies. And last the resulting bugs would not be easy to catch as they would probably not throw exceptions but more likely just cause unwanted behavior changes.
So even if we implement it, I recommend to stick with non-static settings() calls as much as you can so it's scoped to your source code and don't apply to vendor code.
Maybe you can:
class IntervalService
{
public function wrapInterval(mixed $value): CarbonInterval
{
if (!($value instanceof CarbonInterval)) {
return $value;
}
return $value->settings([
'toStringFormat' => static fn($interval) => $interval->spec(),
]);
}
public function __call(string $name, array $args)
{
return $this->wrapInterval(CarbonInterval::$name(...$args));
}
}
$interval = new IntervalService();
echo $interval->fromString('2 days 5 hours'); // P1DT5H
echo $interval->wrapInterval(Carbon::parse('2022-08-27')->diffAsCarbonInterval('2022-08-29')); // P2D
This way instead of directly creating CarbonInterval
objects from static constructors, you create them via your application factory (or wrap a result) which is then responsible for giving the settings they need for your app.
This is also more scalable as if you need at some point to extract part of your code to an other application, you won't have the global settings to be ported, or if you merge code from other place that would rely on a different __toString
it won't collide.
@kylekatarnls I agree with you, thank you for pointing it out and thank you for the edit. Currently, what I could do (taking your comment into account) is:
// Laravel Cast class
public function set($model, string $key, $value, array $attributes)
{
CarbonInterval::setToStringFormat(function (CarbonInterval $interval) {
return $interval->spec();
});
$casted = $value->toJson();
CarbonInterval::setToStringFormat(null);
return is_null($value) ? null : $value->toJson();
}
Also, I can setToStringFormat()
on creation, because once the above $value
exists, I can't know what it contains but the wrapper is a great idea, thank you
->settings()
and ::setToStringFormat()
will work in 2.63.0 on CarbonInterval and CarbonPeriod as they do now for Carbon and CarbonImmutable.
You can test it using composer require "nesbot/carbon:dev-master as 2.63.0"
Hello,
Is there a way to globally change the
CarbonInterval::__toString()
format? I'm usingCarbonInterval
in a generic class which type juggle to string when an attribute isStringable
.Since
PHP 8
,CarbonInterval
isStringable
, so will be converted to string in my class, which currently defaults to$this->forHuman()
. I would like to set globally something like:Is there a way to do that please?
Carbon version: 2.62.0
PHP version: 8.1.0
Thanks!
@kylekatarnls edit: replacing
CarbonInterface
occurrences withCarbonInterval
as it sounds it was the intended meaning.