Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Context Superclass – Improve Modularity and Testability #399

Open
4 tasks
m1roff opened this issue Feb 10, 2025 · 1 comment
Open
4 tasks

Remove Context Superclass – Improve Modularity and Testability #399

m1roff opened this issue Feb 10, 2025 · 1 comment

Comments

@m1roff
Copy link

m1roff commented Feb 10, 2025

Overview/summary

The Context class in the Shopify PHP API library introduces global state, making the codebase harder to test, maintain, and extend. It violates multiple SOLID principles, specifically Single Responsibility Principle (SRP) and Dependency Inversion Principle (DIP). This issue proposes refactoring to remove Context and replace it with dependency injection.

Motivation

Currently, many classes depend on Context for critical API information like:
• API keys ($API_KEY, $API_SECRET_KEY)
• Session storage ($SESSION_STORAGE)
• HTTP Client Factory ($HTTP_CLIENT_FACTORY)
• API version ($API_VERSION)
• Retry logic ($RETRY_TIME_IN_SECONDS)

This leads to tightly coupled code, where:
1. Global state makes debugging difficult – Modifying Context affects all instances, leading to unpredictable behavior.
2. Unit testing is complicated – Tests must mock the static Context state rather than injecting dependencies.
3. Extensibility is restricted – Developers cannot easily replace components like HTTP clients or storage mechanisms.

Area

The Context dependency is spread across multiple classes. Some key examples:

  1. Graphql Client

Problem: Uses Context::$IS_PRIVATE_APP and Context::$API_VERSION, making it impossible to instantiate Graphql client without relying on Context.

if (!Context::$IS_PRIVATE_APP && empty($token)) {
    throw new MissingArgumentException('Missing access token when creating GraphQL client');
}

Proposed Fix: Inject Configuration object instead of relying on Context:

class Graphql {
    public function __construct(
        private readonly Configuration $config,
        private readonly HttpClient $httpClient,
        private readonly ?string $token = null
    ) { }
}
  1. Http Client

Problem: Uses Context::$HTTP_CLIENT_FACTORY and Context::$USER_AGENT_PREFIX to generate requests.

$client = Context::$HTTP_CLIENT_FACTORY->client();

Proposed Fix: Pass HttpClientFactory and UserAgentProvider via constructor:

class Http {
    public function __construct(
        private readonly HttpClientFactory $clientFactory,
        private readonly UserAgentProvider $userAgentProvider
    ) { }
}

Checklist

  • Refactor Context into a Configuration object that can be injected.
  • Update Graphql, Http, and other dependent classes to use dependency injection.
  • Ensure backward compatibility where possible.
  • Improve testability by allowing mock injection.
@m1roff
Copy link
Author

m1roff commented Feb 12, 2025

I started making changes in my Fork https://github.com/m1roff/shopify-api-php/pull/1/files, if it is interesting for someone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant