Welcome to Abdul Malik Ikhsan's Blog

My Approach to Working with 8GB RAM as a PHP Developer

Posted in php, Teknologi by samsonasik on November 15, 2023

Image

I have been a macOS user for more than a decade. Despite this extensive period of use, there are still many tweaking tricks that I am not familiar with. This post aims to share how I optimize my experience with the knowledge I have, all while maintaining usability. If you notice anything that I’ve done incorrectly, feel free to correct me :).

Here are what I do:

1. Delete unused files periodically.

I contribute to open-source projects. A lot. Most of the files are on Github. When I no longer actively work on a specific repository, I just remove it locally and clean up from the trash. When I need it, I re-clone it. This allows me to have only about 20% of my storage filled:

Image

From what I understand, this will enable macOS to write to the SSD more randomly, making everything snappy.

2. Create directories under ~/Downloads for different file types

This will allow me to easily identify files that I believe are still okay to be there and quickly look them up when needed.

Image

Periodically, I can review the downloaded files to identify those that are no longer needed and remove them.

3. Open fewer applications simultaneously

With 8GB of memory, it is indeed pretty limited in terms of what I can do simultaneously. Fortunately, I am not a multitasker, and I often only hit swap when running a specific long-running process (usually on CLI) while I work on other things as well, which is fine if actually needed.

What I open when I code?

  • A Code Editor (Visual Studio Code: a less memory-intensive one but capable enough for some features I want, e.g., tab completion). I open a more memory-hungry one, PhpStorm, only when I need to debug unknown code or type-hinting code that is prefixed.
  • Browser: Firefox
  • Terminal

Yes, only the above. I don’t open Whatsapp or Twitter (X) as dedicated applications; I just open them in the browser when I need them, and close. No more free unused tabs.

When I no longer use specific applications, I ensure to quit them:

Image

4. I disable notifications

I disable most of notification that I know not needed, and I can just open periodically when I want.

Image

5. Prevent Spotlight from searching “www”

I named a directory “www” for project files, which consists of PHP projects with vendor included, which is a lot and changes very often. That allows me to have a low re-index process.

Image

6. Disable iCloud service

I mostly use it when I want to pull something from iCloud, and it is very rare; I pull, then sign out.

7. Disable Siri

I don’t use Siri, so I disable it.

8. Clean up Login Items

I ensure that login items is limited.

Image

9. Disable Database service

I rarely use a database service, so I just disable it as well.

10. Use PHP’s built-in web server

I don’t use dedicated web server, as PHP’s built-in web server is enough for me:

php -S localhost:8080 -t public

11. Using docker only when project require it

I only install Docker when I need it; other than that, I just remove it, clearing up all the containers!

That’s it πŸ˜‰

Handle Slow PHP cli with IMAP extension on macOS

Posted in hack, php by samsonasik on September 30, 2023

About two weeks ago, I installed a PHP project that required the IMAP extension. I installed it via MacPorts, and everything seemed okay. However, after that, I experienced very slow execution when running PHP via the CLI.

I am using macOS Monterey, which was released in 2021. It turns out that the issue has existed since 2016 on macOS Sierra. This was discussed on Stack Overflow in 2017:

The issue has already been reported to php-src:

I couldn’t reproduce the issue on Linux Ubuntu 22.04.

Following the Stack Overflow answer mentioned above, I tried to add my hostname to the /etc/hosts file. First, I obtained the hostname:

➜  ~ hostname

Abduls-Mac-mini.local

My original /etc/hosts file had the following entries:

127.0.0.1   localhost
::1         localhost

Then, I added the hostname to the third column on each row:

127.0.0.1   localhost Abduls-Mac-mini.local
::1         localhost Abduls-Mac-mini.local

After that, the issue was resolved, and it became fast again. That’s it! πŸ˜‰

References:

  1. https://github.com/php/php-src/issues/11673
  2. https://stackoverflow.com/questions/44878887/mac-os-sierra-slow-work-php-imap-cli

How to Update to PHP 7.4 Typed Property Without BC Break with Rector

Posted in php, Rector, Teknologi by samsonasik on September 29, 2021

In Rector 0.12.9, `TypedPropertyRector` is not configurable, it will only change:

– private property
– protected property on final class without extends

In Rector 0.12.16, `TypedPropertyRector` has configurable to allow change protected and public modifier as well as far when possible with new configurable:

   $services->set(TypedPropertyRector::class)
        ->configure([
            TypedPropertyRector::INLINE_PUBLIC => true,
        ]);

This Article is valid for Rector <= 0.12.8

Typed Property is one of the PHP 7.4 feature that allow to write that previously like this:

namespace Lib;

class SomeClass
{
    /** @var int */
    public $a;

    /** @var string */
    protected $b;

    /** @var bool */
    private $c;
}

to this:

namespace Lib;

class SomeClass
{
    public int $a;

    protected string $b;

    private bool $c;
}

If you follow Semver for versioning, and you don’t want to update to major change, eg: version 1 to version 2, changing this will make Break Backward Compatibility, for example:

namespace Lib;

class SomeClass
{
    protected string $b;
}

has child in application consumer:

namespace App;

use Lib\SomeClass;

class AChild extends SomeClass
{
    protected $b;
}

will result a fatal error:

Fatal error: Type of AChild::$b must be string (as in class SomeClass)

see https://3v4l.org/X9Yvd . To avoid that, you should only change to private modifier only, so, the change will only to private property:

namespace Lib;

class SomeClass
{
    /** @var int */
    public $a;

    /** @var string */
    protected $b;

-    /** @var bool */
-    private $c;
+    private bool $c;
}

Want to automate that? You can use Rector for it. First, let say, we have a re-usable package that can be consumed in our applications, with the following package structure:

lib
β”œβ”€β”€ composer.json
β”œβ”€β”€ composer.lock
β”œβ”€β”€ src
β”‚   └── SomeClass.php

with composer.json config like this:

{
    "require": {
        "php": "^7.4"
    },
    "autoload": {
        "psr-4": {
            "Lib\\": "src/"
        }
    }
}

Your package will be hosted in packagist or your own server.

Now, what you need is require the rector as dev dependency by go to lib directory:

cd lib/
composer require --dev rector/rector

Rector has rule named TypedPropertyRector, that part of SetList::PHP_74 constant.

It default will update all modifiers:

  • public
  • protected
  • private

If you are using on projects that not re-usable project, you can just use SetList::PHP_74 constant as is.

For our use case, you can override it by configure it to only apply to private property only.

You can create a rector.php configuration inside the root of lib directory as follow:

<?php

declare(strict_types=1);

use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Php74\Rector\Property\TypedPropertyRector;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();
    $parameters->set(Option::PATHS, [
        __DIR__ . '/src'
    ]);
    $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74);

    // import php 7.4 set list for php 7.4 features
    $containerConfigurator->import(SetList::PHP_74);

    // set Typed Property only for private property
    $services = $containerConfigurator->services();
    $services->set(TypedPropertyRector::class)
        ->call('configure', [[
            TypedPropertyRector::PRIVATE_PROPERTY_ONLY => true,
        ]]);
};

Above, we import php 7.4 set list, with configured TypedPropertyRector for update to typed property to only change private property only.

Now, let’s run rector to see the diff and verify:

cd lib
vendor/bin/rector --dry-run

Image

Everything seems correct! Let’s apply the change:

cd lib
vendor/bin/rector

Image

Now, you have typed property in your code!

That’s it!

Install php 8.0 in MacOS Sierra with Macports

Posted in php, Teknologi by samsonasik on December 6, 2020

Yes, you read it right. I live in the past, with old hardware, with legacy operating system. There is homebrew that previously still works in php 7.4 in early releases, but now no longer works. There is an alternative like a one click install app like MAMP PRO but didn’t support php 8.0 yet. XAMPP ? not really extension installable user friendly (as far as I tried)!

I don’t want to lost hope, I give a Macports a try, and it works while it not documented in the wiki yet. If you are using MacOS Sierra and want to try, you can do the following steps:

1. First, you need to have Macports installed first, that we can read in the documentation.
2. Install php 8.0

sudo port install php80

You will get the following note in the end of installation process:

 To customize php80, copy /opt/local/etc/php80/php.ini-development (if this is a development server) or /opt/local/etc/php80/php.ini-production (if this is a production server) to /opt/local/etc/php80/php.ini and then make changes.

You can follow that if you want to changes the PHP configuration.

3. Install common extensions

For example, you will need intl and mbstring extension, you can run:

sudo port install php80-intl
sudo port install php80-mbstring 

You can search already available extensions in the search page.

4. Now, you can check if it is actually installed:

php80 --version

Image

That’s it πŸ˜‰

How to Create Typo Variable Fixer with Rector

Posted in php, Rector, Teknologi by samsonasik on October 21, 2020

Rector is a code refactoring tool that can help us with major code changes (like upgrade legacy code) or daily work. There are already many rules that ready to use for us.

What if we want a custom rule, like we want a daily work can to do “Typo” check in variables? In this post, I want to show you how to create a Typo Variable Fixer with Rector, a custom Rector rule!

Preparation

First, let say, we build a new app, we use composer for it with add rector/rector to require-dev:

composer init


  Welcome to the Composer config generator



This command will guide you through creating your composer.json config.

Package name (<vendor>/<name>) [samsonasik/how-to-create-typo-variable-fixer]: samsonasik/app

Description []: App Demo

Author [Abdul Malik Ikhsan <[email protected]>, n to skip]:

Minimum Stability []:

Package Type (e.g. library, project, metapackage, composer-plugin) []:

License []: MIT

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? no
Would you like to define your dev dependencies (require-dev) interactively [yes]? yes
Search for a package: rector/rector
Enter the version constraint to require (or leave blank to use the latest version):

Using version ^0.8.40 for rector/rector

Search for a package:

{
    "name": "samsonasik/app",
    "description": "App Demo",
    "require-dev": {
        "rector/rector": "^0.8.40"
    },
    "license": "MIT",
    "authors": [
        {
            "name": "Abdul Malik Ikhsan",
            "email": "[email protected]"
        }
    ],
    "require": {}
}

Do you confirm generation [yes]? yes
Would you like to install dependencies now [yes]? yes

After it, let say we need an app directory, we can create an app directory and write a php file inside it:

mkdir -p app && touch app/app.php

with file app/app.php content:

<?php

namespace App;

$previuos = 0;
$begining = 1;
$statment = $previuos . ' is lower than ' . $begining;

Yes, there are 3 typos in above file! For example, we will have a sample library.php file for common typos, for example, inside utils directory:

mkdir -p utils && touch utils/library.php

with file utils/library.php content:

<?php

namespace Utils;

return [
    'previous' => ['previuos', 'previuous'],
    'beginning' => ['begining', 'beginign'],
    'statement' => ['statment'],
];

We can setup composer autoload for with add the following to our composer.json file:

    "autoload": {
        "psr-4": {
            "App\\": "app"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Utils\\": "utils"
        }
    }

After it, run composer dump-autoload command:

composer dump-autoload

The preparation is done!

Create the Typo Fixer Rule

We can follow the Rector‘s documentation to create new custom rule. So, for example, we create TypoVariableFixerRule under own utils/Rector directory:

mkdir -p utils/Rector && touch utils/Rector/TypoVariableFixerRule.php

Our directory will looks like the following:

.
β”œβ”€β”€ app
β”‚   └── app.php
β”œβ”€β”€ composer.json
β”œβ”€β”€ utils
β”‚   β”œβ”€β”€ Rector
β”‚   β”‚   └── TypoVariableFixerRule.php
β”‚   └── library.php

Now, we can start create the TypoVariableFixerRule:

<?php

declare(strict_types=1);

namespace Utils\Rector;

use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;

final class TypoVariableFixerRule extends AbstractRector
{
    public function getNodeTypes(): array
    {
        return [Variable::class];
    }

    /**
     * @param Variable $node
     */
    public function refactor(Node $node): ?Node
    {
        return $node;
    }

    public function getDefinition(): RectorDefinition
    {
        return new RectorDefinition(
            'Change Typo in variable', [
                new CodeSample(
                    // code before
                    '$previuos',
                    // code after
                    '$previous'
                ),
            ]
        );
    }
}

Above, we extends AbstractRector for new Rector rule. We operate with nikic/php-parser to do refactor. The getNodeTypes returns the node that we want to refactor, at this case, we want to refactor Variable node in our refactor method.

Before we continue, let’s register our new TypoVariableFixerRule to rector config to ensure it works. We can create rector config as follow:

touch rector.php

with file rector.php content:

<?php

use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Utils\Rector\TypoVariableFixerRule;

return static function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();
    $parameters->set(Option::PATHS, [__DIR__ . '/app']);

    $services = $containerConfigurator->services();
    $services->set(TypoVariableFixerRule::class);
};

and test with run:

vendor/bin/rector process

So we see the “Green” OK:

Image

Now, time to make refactor work! We can modify the refactor method:

    public function refactor(Node $node): ?Node
    {
        // get the variable name
        $variableName = $this->getName($node);

        // get the library content
        $library = include 'utils/library.php';

        foreach ($library as $correctWord => $commonTypos) {
            if (! in_array($variableName, $commonTypos, true)) {
                continue;
            }

            $node->name = $correctWord;
            return $node;
        }

        return null;
    }

Above, we find if the variable name is in common typos, then we return node (as variable) with updated its name with the correct word. Now, let’s run it with --dry-run to see the diff that can be made:

vendor/bin/rector process --dry-run

and we can see:

Image

Seems great! Let’s apply the changes:

vendor/bin/rector process

Image

Awesome! We now already make typo fixer succesfully working! Let’s run again, and it will take no effect as already fixed:

Image

That’s it!

Call private/protected method without ReflectionMethod in PHP

Posted in php by samsonasik on July 21, 2019

For whatever reason, you may need to call private/protected method from an object. For example, we have a Foo class with bar method with private modifier like below:

class Foo
{
    private function bar()
    {
        echo 'hit';
    }
}

We need to call bar() method from an instance of Foo class. If you are familiar with ReflectionMethod, you may do this:

$r = new ReflectionMethod($foo = new Foo(), 'bar');
$r->setAccessible(true);
$r->invoke($foo);

There is another way. It is by using invoked of Closure::bindTo() like below:

(function ($foo) {
    $foo->bar();
})->bindTo($foo = new Foo(), Foo::class)($foo);

That’s it!

Tagged with: , ,

Tinify : Resize your image without reduce quality

Posted in php, Teknologi, tips and tricks by samsonasik on February 3, 2019

I tried some client library services for my project to resize images. They resized images correctly, however, they made the images quality slightly reduced. Until my client suggested me to try Tinify API service, that the API key I can just use, and it works pretty well.

The Tinify provide free 500 compresions per-month if you need to try. You can upgrade to paid account if you need more. To register, you can go to https://tinypng.com/developers and got the following page and fill your name and email and click “Get your API key”:

Image

You can then check your email to get the link and be redirected to the dashboard page with information of your API key and how many compressions left for current month:

Image

There are libraries for different languages, for example, if you are using PHP, you can use tinify-php that you can require via composer in your project by run:

$ composer require tinify/tinify

Once required, you can do :

include 'vendor/autoload.php';

\Tinify\setKey('YOUR_API_KEY'); // fill with your API key

$source = \Tinify\fromFile('/path/to/file.png'); // source file
$resized = $source->resize(array(
    'method' => 'scale',
    'width'  => 500,
));
$resized->toFile('target-thumbnail.png'); // target file

For complete reference, you can look at https://tinypng.com/developers/reference/php . That’s it!

Using conflict in composer.json for BC Break handling on Optional feature

Posted in hack, php by samsonasik on January 11, 2019

You may have a PHP library that can work with multiple project skeletons with optional feature, so the dependencies are not required. For example, your composer.json library looks like:

{
    "name": "your/library",
    "require": {
        "php": "^7.1",
    },
    "require-dev": {
        "foo/bar": "^2.0",
        "phpunit/phpunit": "^7.0"
    },
    "suggest": {
        "foo/bar": "^2.0 usage via Your\\Library\\Adapter\\Foo\\Bar adapter"
    }
}

The “foo/bar” library is an optional library which used in “your/library” that consume it, collected in the require-dev above for testing purpose.

The “foo/bar” library has class “Foo\Bar\Way”. There are 2 versions of “foo/bar” lib, which version 1 and version 2 have different signature as follow:

a. “foo/bar” version 1 of “Foo\Bar\Way”

namespace Foo\Bar;

class Way
{
    public function execute(string $a, string $b)
    {
        // ...
    }
}

b. “foo/bar” version 2 of “Foo\Bar\Way”

namespace Foo\Bar;

use stdClass;

class Way
{
    public function execute(stdClass $std)
    {
        // ...
    }
}

In above code, if current user are using foo/bar:^1.0, the user code will be break when user upgrade “your/library” to version 2.0. So, to avoid that, you can restrict it via “conflict”, as follow:

{
    "name": "your/library",
    "require": {
        "php": "^7.1",
    },
    "require-dev": {
        "foo/bar": "^2.0",
        "phpunit/phpunit": "^7.0"
    },
    "suggest": {
        "foo/bar": "^2.0 usage via Your\\Library\\Adapter\\Foo\\Bar adapter"
    },
    "conflict": {
        "foo/bar": "<2.0"
    }
}

Now, you can tag “your/library” as “2.0” and user won’t be allowed to install new tagged library if they still uses “foo/bar”:”^1.0″ in existing project, That’s it!

Replace Hard Dependency Class with New Simulation Class via “replace” and “classmap” in Composer Configuration

Posted in hack, php by samsonasik on October 15, 2018

If we are using 3rd party library that managed by composer, which has hard dependency that we don’t want to use, for example, at the following use case:

The one of the solutions for that is by using “replace” and “classmap” configuration in our composer.json. First, we need to prepare of the class to simulate the Logger class, for example, we have it in src/App/Apache/Logger.php:

<?php
// src/App/Apache/Logger.php

class Logger 
{
    function debug(...$args) {}
    function info(...$args) {}
    function trace(...$args) {}
    function warn(...$args) {}
    function error(...$args) {}
    function fatal(...$args) {}

    public static function configure(...$args) {}
    public static function getLogger() { return new self(); }
}

Yes, above class doesn’t do anything, for silent action when Logger::{themethod()} called in realexpayments/rxp-remote-php library classes.

Next, we can register it to our composer.json:

{
    "require": {
        // ...
        "realexpayments/rxp-remote-php": "^1.2"
        // ...
    },
    "replace": {
        "apache/log4php": "^2.3.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/App/",
        },
        "classmap": [
            "src/App/Apache/Logger.php"
        ]
    }
}   

In above configuration, the replace of “apache/log4php” doesn’t has replacement library in ‘require’ part will make the dependency removed entirely as we don’t want to use it anymore, and by the classmap configuration, we have new redefined of the Logger class as simulation of “apache/log4php” Logger class.

Last step, we can run:

➜ composer update

That’s it!

Using “no-api” key on Github Forked Repository in Composer repositories configuration for Travis Build

Posted in hack, php by samsonasik on October 7, 2018

So, you’re doing a pull request, you want the feature to be merged so bad to public repository, but for whatever reason, it is not merged yet. You can register your forked repository under composer “repositories” config:

{
    "require": {
        "awesome/library": "dev-my-fork-awesome-feature"
    },
    "repositories" : [
        {
            "type" : "vcs",
            "url" : "https://github.com/yourgithubuser/library"
        }
    ]
}

That will work for your local dev and your server, but unfortunatelly, that won’t work on Travis! We will get the following error:

Failed to clone the [email protected]:yourgithubuser/library.git repository, try running in interactive mode so that you can enter your GitHub credentials
                                                                                                                                                                                      
  [RuntimeException]                                                                                                                                                                  
  Failed to execute git clone --mirror '[email protected]:yourgithubuser/library.git' '/home/travis/.composer/cache/vcs/git-github.com-yourgithubuser-library.git/'  

To make that work, we will need the “no-api” key in your composer.json under the per-repository inside “repositories”, as follow:

// ...
        {
            "type" : "vcs",
            "url" : "https://github.com/yourgithubuser/library",
            "no-api": true
        }
// ...

Now, our composer.json will look like this:

{
    "require": {
        "awesome/library": "dev-my-fork-awesome-feature"
    },
    "repositories" : [
        {
            "type" : "vcs",
            "url" : "https://github.com/yourgithubuser/library",
            "no-api": true
        }
    ]
}

That’s it!

Introduce IsDeprecated: PHP7+ Helper for E_USER_DEPRECATED and E_DEPRECATED Detection

Posted in hack, php, Teknologi by samsonasik on January 18, 2018

Before I continue, allow me to give you my suggestions:

Live with deprecated function is bad, you may can’t upgrade to newer PHP version or newer library as the function that was deprecated already removed in next major/minor version.

Use same environment or at least same major and minor version between local dev and production environment when possible, your life will be easier.

When reality is not always what you want

You may found a a reality when you work at different version of PHP version or library that rely on specific function which already deprecated in some environment, the IsDeprecated may be solution at that time that can verify E_USER_DEPRECATED or E_DEPRECATED trigger error. It utilize jeremeamia/FunctionParser for user defined function check, and zendframework/zend-stdlib’s ErrorHandler for E_DEPRECATED function check.

When you found the passed function is deprecated, you can use alternative function.

Installation

This helper can be installed via composer:

composer require samsonasik/is-deprecated

This helper have features:

1. Detect E_USER_DEPRECATED

  • At independent function
  • At function inside class

You can use IsDeprecated\isDeprecatedUser function with signature:

/**
 * @param  string|array $function the "functionName" or ["ClassName" or object, "functionName"] or "ClassName::functionName"
 * @throws InvalidArgumentException when trigger_error found but the error is not E_USER_DEPRECATED
 * @throws InvalidArgumentException when trigger_error and E_USER_DEPRECATED found but misplaced
 * @return bool
 */
function isDeprecatedUser($function): bool

Note: when trigger_error E_USER_DEPRECATED inside condition, you need to use actual call with signature:

/**
 * @param  callable $function callable function
 * @return bool
 */
function isDeprecatedWithActualCall(callable $function)

1a. Independent Function

The usage is like the following:

use function IsDeprecated\isDeprecatedUser;

function foo()
{
    trigger_error('this method has been deprecated.', E_USER_DEPRECATED);
}

if (isDeprecatedUser('foo')) {
    // apply alternative/new function to call...
} else {
    foo();
}

1.b Function Inside Class

The usage is like the following:

use function IsDeprecated\isDeprecatedUser;

class AClass
{
    public function foo()
    {
        trigger_error('this method has been deprecated.', E_USER_DEPRECATED);
    }

    // check inside with $this
    public function execute()
    {
        if (isDeprecatedUser([$this, 'foo'])) {
             // apply alternative/new function to call...
             return;
        }

        $this->foo();
    }
}

// you may call after instantiation
$object = new \AClass();
if (isDeprecatedUser([$object, 'foo'])) {
    // apply alternative/new function to call...
} else {
    $object->foo();
}

// or
if (isDeprecatedUser(['AClass', 'foo'])) {
    // apply alternative/new function to call...
} else {
    (new \AClass())->foo();
}

2. Detect E_DEPRECATED

E_DEPRECATED can be triggered on Core PHP function call.

You can use IsDeprecated\isDeprecatedCore function with signature:

/**
 * @param  callable $function callable function
 * @return bool
 */
function isDeprecatedCore(callable $function): bool

The usage is like the following:

use function IsDeprecated\isDeprecatedCore;

$function = function () {
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
};

if (isDeprecatedCore($function)) {
    // alternative function, eg: openssl ...
} else {
    $function();
}

Limitation

For Core PHP Functions or user function with condition (T_IF or T_SWITCH token), the function passed actually need to be called. It ensure that we don’t get error during call deprecated function, and we can use alternative function if the isDeprecatedCore() returns true with call of isDeprecatedWithActualCall.

You want to use it? You can check my repository https://github.com/samsonasik/IsDeprecated

That’s it ;).