public function getProperties(?int $filter = null) : array
    {
        if ($this->cachedProperties === null) {
            // merging together properties from parent class, traits, current class (in this precise order)
            $this->cachedProperties = array_merge(
                array_merge(
                    [],
                    ...array_map(
                        static function (ReflectionClass $ancestor) use ($filter) : array {
                            return array_filter(
                                $ancestor->getProperties($filter),
                                static function (ReflectionProperty $property) : bool {
                                    return ! $property->isPrivate();
                                }
                            );
                        },
                        array_filter([$this->getParentClass()])
                    ),
                    ...array_map(
                        function (ReflectionClass $trait) use ($filter) {
                            return array_map(function (ReflectionProperty $property) use ($trait) : ReflectionProperty {
                                return ReflectionProperty::createFromNode(
                                    $this->reflector,
                                    $property->getAst(),
                                    $property->getPositionInAst(),
                                    $trait->declaringNamespace,
                                    $property->getDeclaringClass(),
                                    $this
                                );
                            }, $trait->getProperties($filter));
                        },
                        $this->getTraits()
                    )
                ),
                $this->getImmediateProperties()
            );
        }

        if ($filter === null) {
            return $this->cachedProperties;
        }

        return array_filter(
            $this->cachedProperties,
            static function (ReflectionProperty $property) use ($filter) : bool {
                return (bool) ($filter & $property->getModifiers());
            }
        );
    }