strategy pattern, good practices on how to switch between classes
You are probably looking for factory method pattern, instead of strategy pattern.

The CustomItemSender class is a good indicator of this.
Whenever you extend a class to only override the constructor, you probably need a factory instead. It means you need somewhat more complex way of constructing the object and that implies creational pattern. Strategy is a behavioral pattern (and in fact, your code does not implement this pattern).

The EnvironmentInterface is basicaly a data structure, it is immutable which is good, but it has no behaviour, which is not so good. It is just a tuple. Maybe you don't really need this structure and stay with tripplet of arguments.

Polymorphism only makes sense to abstract varying behaviour, not data. Anyway polymorphism won't help you avoid ifs/switches entirely. It will help you reduce the amount of them to minimum. You might still need some ifs in the creational part, but once a polymorhic object is created, it no longer repeats those ifs because it encapsulates the beforehand chosen branch.

Sometimes ifs/switches can be replaced by key-value maps though (such a map could be owned by a factory...).

Getting config values from a file using global function like yours getFromConfigFile(string $key) is not very wise btw. Because it either loads the file again on every request for a key. Or it loads it only once but stores the loaded data to a global variable thus having side effects on globle state which is a bad practice. You might want to load all config from the file to lets say array (possibly encapsulated in an objet) and serve the individual config values from it. And btw the function knows the location of the config file globally/magically and that is also not very good. You might need a factory for the config object, which will load the config from a given a file.

function loadConfigFromJsonFile(string $filename): array
{
  // add error handling
  return json_decode(file_get_contents($filename), true);
}

class SenderFactory
{

  private string $endpoint;
  private string $user;
  private string $password;

  public function __construct(string $endpoint, string $user, string $password)
  {
    $this-&gt;endpoint = $endpoint;
    $this-&gt;user = $user;
    $this-&gt;password = $password;
  }

  public function createOrderSender(): OrderSender
  {
     return new OrderSender($this-&gt;endpoint, $this-&gt;user, $this-&gt;password);
  }

  public function createItemSender(): ItemSender
  {
     return new ItemSender($this-&gt;endpoint, $this-&gt;user, $this-&gt;password);
  }
}

function createSenderFactory(string $env, array $config): SenderFactory
{
  // the if has to go somewhere anyway
  // add some error handling too (missing config keys, unknown $env, etc.)
  if ($env === 'testing') {
    return new SenderFactory($config['API_ENDPOINT_TESTING'], 'testing', 'testing');
  } else {
    return new SenderFactory($config['API_ENDPOINT_PRODUCTION'], 'production', 'production');  
  }
}


// ...


$config = loadConfigFromJsonFile($configPath);
$factory = createSenderFactory($env, $config);
$orderSender = $factory-&gt;createOrderSender();
$itemSender = $factory-&gt;createItemSender();
```