Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions src/helpers/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use craft\web\User as WebUser;
use craft\web\View;
use HTMLPurifier_Encoder;
use Illuminate\Support\Collection;
use ReflectionClass;
use ReflectionFunction;
use ReflectionNamedType;
Expand All @@ -48,6 +49,7 @@
use yii\base\Exception;
use yii\base\InvalidArgumentException;
use yii\base\InvalidValueException;
use yii\base\Model;
use yii\db\Exception as DbException;
use yii\helpers\Inflector;
use yii\mutex\FileMutex;
Expand Down Expand Up @@ -166,44 +168,65 @@ private static function parseNestedEnv(string $value): string
* For example, if an object has a `fooBar` property, and `X`/`X_` is passed as the prefix, the resulting array
* may contain a `fooBar` key set to an `X_FOO_BAR` environment variable value, if it exists.
*
* @param class-string $class The class name
* @param object|string $class The class name or object
* @phpstan-param class-string|object $class
* @param string|null $envPrefix The environment variable name prefix
Comment on lines +171 to 173
* @return array
* @phpstan-return array<string, mixed>
* @since 4.0.0
*/
public static function envConfig(string $class, ?string $envPrefix = null): array
public static function envConfig(object|string $class, ?string $envPrefix = null): array
{
$envPrefix = $envPrefix !== null ? StringHelper::ensureRight($envPrefix, '_') : '';
$properties = (new ReflectionClass($class))->getProperties(ReflectionProperty::IS_PUBLIC);
$envConfig = [];

foreach ($properties as $prop) {
if ($prop->isStatic()) {
continue;
}

$envName = null;

foreach ($prop->getAttributes(EnvName::class) as $attribute) {
/** @var EnvName $envName */
$envName = $attribute->newInstance();
$envName = $envName->name;
break;
}

if (!$envName) {
$envName = strtoupper(StringHelper::toSnakeCase($prop->getName()));
}
$reflection = new ReflectionClass($class);
$isModel = $reflection->isSubclassOf(Model::class);

/** @var ?Model $model */
$model = $isModel
? ($class instanceof Model ? $class : Craft::createObject($class))
: null;

$properties = $model
? Collection::make($model->attributes())
: Collection::make($reflection->getProperties(ReflectionProperty::IS_PUBLIC))
->filter(fn(ReflectionProperty $prop) => !$prop->isStatic())
->map(fn(ReflectionProperty $prop) => $prop->getName());

return $properties
->mapWithKeys(function(string $propName) use ($envPrefix, $reflection) {
$envName = static::envNameForProperty($reflection, $propName)
?? strtoupper(StringHelper::toSnakeCase($propName));
$envValue = static::env($envPrefix . $envName);

return [$propName => $envValue];
})
->whereNotNull()
->all();
}

$envValue = static::env(sprintf('%s%s', $envPrefix, $envName));
/**
* Returns the environment variable name defined by a property’s `EnvName` attribute, if it has one.
*
* @param ReflectionClass $reflection
* @phpstan-param ReflectionClass<object> $reflection
* @param string $propName
* @return string|null
*/
private static function envNameForProperty(ReflectionClass $reflection, string $propName): ?string
{
// CONFLICT-REVIEW: model attributes() can include names that aren’t backed by a real property
// (behaviors, magic properties), so only reflect on the ones that are.
Comment on lines +217 to +218
if (!$reflection->hasProperty($propName)) {
return null;
}

if ($envValue !== null) {
$envConfig[$prop->getName()] = $envValue;
}
foreach ($reflection->getProperty($propName)->getAttributes(EnvName::class) as $attribute) {
/** @var EnvName $envName */
$envName = $attribute->newInstance();
return $envName->name;
}

return $envConfig;
return null;
}

/**
Expand Down