From the About section of nesbot/carbon package on GitHub:
- A simple PHP API extension for DateTime.
It is an extension of PHP's native DateTime
object. It adds lots of cool stuff onto DateTime
and makes it easier to do date and time manipulations.
Let's see how Laravel uses this package to provide even more simpler abstraction such as:
$now = now(); // returns current date and time as Carbon instance
Code Dive
Let's start from definition of now()
method. It can be found in Illuminate\Foundation\helpers.php
file.
use Illuminate\Support\Facades\Date;
/**
* Create a new Carbon instance for the current time.
*
* @param \DateTimeZone|string|null $tz
* @return \Illuminate\Support\Carbon
*/
function now($tz = null)
{
return Date::now($tz);
}
We can see that its calling now()
method on Date
facade.
Date facade
namespace Illuminate\Support\Facades;
use Illuminate\Support\DateFactory;
class Date extends Facade
{
const DEFAULT_FACADE = DateFactory::class;
/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
return 'date';
}
/**
* Resolve the facade root instance from the container.
*
* @param string $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (! isset(static::$resolvedInstance[$name]) &&
! isset(static::$app, static::$app[$name])) {
$class = static::DEFAULT_FACADE;
static::swap(new $class);
}
return parent::resolveFacadeInstance($name);
}
}
We have a typical getFacadeAccessor
method that returns a string (date
). But there are few other things that we haven't seen before. const DEFAULT_FACADE
and resolveFacadeInstance
method.
Since this is not a module like Log
or Scheduling
etc, there is no separate ServiceProvider for this to perform service container binding. Therefore inside resolveFacadeInstance
method it is swapping the underlying instance behind Date
facade to DateFactory
class.
We can also confirm this by using app()
helper like below:
app('date'); // returns instance of `Illuminate\Support\DateFactory` class
DateFactroy class
Below is a ripped off version of DateFactory
class which helps us understand implementation detail behind now()
helper method. This class has other stuff too, see full implementation here.
use Illuminate\Support\Carbon;
class DateFactory
{
/**
* The default class that will be used for all created dates.
*
* @var string
*/
const DEFAULT_CLASS_NAME = Carbon::class;
/**
* The type (class) of dates that should be created.
*
* @var string
*/
protected static $dateClass;
/**
* Handle dynamic calls to generate dates.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \RuntimeException
*/
public function __call($method, $parameters)
{
$defaultClassName = static::DEFAULT_CLASS_NAME;
$dateClass = static::$dateClass ?: $defaultClassName;
// Check if date can be created using public class method...
if (
method_exists($dateClass, $method) ||
$dateClass::hasMacro($method)
) {
return $dateClass::$method(...$parameters);
}
}
}
Since now()
method does not exist on this class and we are also in object
context from Date
facade, therefore, __call()
magic method will be triggered when we do Date::now()
.
Inside __call()
method we can see that $dateClass
is being set to Illuminate\Support\Carbon
class. After that it checks if given method ( now()
) exists on that class or this method is a macro. Once decided, it calls that method on Illuminate\Support\Carbon
class.
Illuminate\Support\Carbon
<?php
namespace Illuminate\Support;
use Carbon\Carbon as BaseCarbon;
use Carbon\CarbonImmutable as BaseCarbonImmutable;
class Carbon extends BaseCarbon
{
}
Here we can see that this class extends Carbon\Carbon
class from nesbot/carbon
package. It can be considered as wrapper class around Carbon\Carbon
which allows you to do interesting stuff like adding Macros.
Finding now() method
With above findings we can safely assume that now()
method should be on Carbon\Carbon
class. But exactly where? Let's find out.
use Carbon\Traits\Date;
class Carbon extends DateTime implements CarbonInterface
{
use Date;
/**
* Returns true if the current class/instance is mutable.
*
* @return bool
*/
public static function isMutable()
{
return true;
}
}
This class extends PHP's native DateTime
class and also uses a Date
trait. Since now()
method does not exist on DateTime
class, it must be inside the Date
trait.
<?php
namespace Carbon\Traits;
use Carbon\Traits\Creator;
trait Date
{
use Creator;
}
To my surprise, it was further inside a trait called Creator
which is being used inside Date
trait.
<?php
namespace Carbon\Traits;
trait Creator
{
/**
* Create a new Carbon instance.
*
* Please see the testing aids section (specifically static::setTestNow())
* for more on the possibility of this constructor returning a test instance.
*
* @param DateTimeInterface|string|null $time
* @param DateTimeZone|string|null $tz
*
* @throws InvalidFormatException
*/
public function __construct($time = null, $tz = null)
{
try {
parent::__construct(
$time ?: 'now',
static::safeCreateDateTimeZone($tz) ?: null
);
} catch (Exception $exception) {
throw new InvalidFormatException(
$exception->getMessage(), 0, $exception
);
}
}
/**
* Get a Carbon instance for the current date and time.
*
* @param DateTimeZone|string|null $tz
*
* @return static
*/
public static function now($tz = null)
{
return new static(null, $tz);
}
}
Here we can finally see the now()
method's definition. It is returning a new Carbon\Carbon
instance using static constructor approach. I included the __construct
method here to highlight the parent::__construct
being called on PHP's native DateTime
class.
Therefore the About section of this package says extension of DateTime
API.
Interesting facts about nesbot/carbon
package in Laravel
- This package was first added in Laravel v4.0 on 19 Apr, 2013 by Kapil Verma
v2
of carbon package was first supported in Laravelv5.8
and then support forv1
of the package was completely dropped in Laravelv6.0
Carbon\Carbon
class wrapped byIlluminate\Support\Carbon
class in Laravelv5.5
to make it Macroable. Pull Request can be found here.
I hope you enjoyed this post. Next, we will see how Laravel uses opis/closure
package. You can follow me on Twitter or join my newsletter for more content.