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

URL Class #4647

Closed
wants to merge 4 commits into from
Closed

URL Class #4647

wants to merge 4 commits into from

Conversation

MGatner
Copy link
Member

@MGatner MGatner commented May 5, 2021

Description
"Stage Three", this PR introduces a companion class to URI: URL. The URL class handles internal URIs (i.e. specific to the project) in the following manner:

  • A URL is always constructed with a relative path
  • A URL will always use the App config values from its construction (baseURL, indexPage, forceGlobalSecureRequest)
  • A URL is immutable, not affected by changes to $_SERVER or services once it has been created

I would like URL to become the definitive source for internal URLs within the framework (e.g. replace the logic in base_url() and current_url(), be used by RedirectResponse, etc) but for now this PR is just introducing the concept and demonstrating the tests. Please be thorough with any reviews, since URLs have been a long pain point (particularly around subfolders).

Note: This PR relies on #4644 and #4646.

Checklist:

  • Securely signed commits
  • Component(s) with PHPdocs
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@lonnieezell
Copy link
Member

Haven't looked at the code yet but my initial thought is - this is making the framework more complex and confusing than it needs to be. Can we just start prepping for a bigger release and fix this in the existing URI class? Have two classes so closely related to each other are going to be super confusing for any devs that dig into the inner workings. Or just to those who's IDE starts to suggest both classes for them.

Additionally - isn't the purpose of site_url to be base_url but taking index.php settings into account?

@MGatner
Copy link
Member Author

MGatner commented May 5, 2021

We really need both. It doesn't need to be implemented this way, but we need to be able to do two things:

  1. Build out a URI representing any valid URI for use anywhere one would want a URI
  2. Build "project-specific" URIs, based off the configuration in App and (potentially) any special routes

Currently this is a real problem:

// If hosted in a sub-folder, we will have additional
// segments that show up prior to the URI path we just
// grabbed from the request, so add it on if necessary.
$config = config(App::class);
$baseUri = new self($config->baseURL);
$basePath = trim($baseUri->getPath(), '/') . '/';
$path = $this->getPath();
$trimPath = ltrim($path, '/');
if ($basePath !== '/' && strpos($trimPath, $basePath) !== 0)
{
$path = $basePath . $trimPath;
}
// force https if needed
if ($config->forceGlobalSecureRequests)
{
$this->setScheme('https');
}

I've tried to mitigate it in #4646 but this "bleeds" point 2 into point 1. URI should not know or care about the framework settings because it makes it impossible to create any URI one might want because the framework settings take over.

Currently we handle "project-specific" URIs with the URL Helper functions (base_url(), site_url(), current_url()) - the problem with these is that the final project has no concept of "what is project" versus "what is path" (e.g. http://example.com/foo/bar - is the baseURL http://example.com/ with "foo/bar" path? or is it http://example.com/foo/ with "bar" path?) so I decided to go with a class solution so we have some way of storing and accessing that info.

I originally had URL as FrameworkURI that was an extension of URI, but there are too many setters in URI that could "violate" an internal URI so I split it out; at that point I realized it was basically a full URL so went with the name, but I'm open to changing it.

@MGatner
Copy link
Member Author

MGatner commented May 5, 2021

isn't the purpose of site_url to be base_url but taking index.php settings into account

Exactly. site_url() amounts to "take base_url(), add App::$indexPage, add any given path, be smart about slashes." URL::to() tries to encompass this behavior but with a little more utility of resolving named and reverse routes.

*/
public static function base()
{
return static::public('');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the class is final, then inheritance is no more possible. So, there's no need to use the static keyword. Instead use self.

Suggested change
return static::public('');
return self::public('');

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the topic, how do you think the class should be scoped? Originally I had a normal class with final static and constructor methods (hence the leftover static references here) but I decided that nobody would go through the hassle of extending for the few allowed methods so made the whole thing final.

$config = clone config('App');
$config->indexPage = '';

return new static($uri, $config);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new static($uri, $config);
return new self($uri, $config);

Comment on lines +70 to +71
$config = clone config('App');
$config->indexPage = '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$config = clone config('App');
$config->indexPage = '';
$config = clone config('App');
$config->indexPage = '';

return self::$current;
}

return static::fromRequest(Services::request());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return static::fromRequest(Services::request());
return self::fromRequest(Services::request());

*/
public static function to(string $uri)
{
return new static(rtrim($uri, '/ '));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new static(rtrim($uri, '/ '));
return new self(rtrim($uri, '/ '));

*
* @param string $uri Additional URI string to include
*
* @return static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return static
* @return self

/**
* Returns an instance representing the current URL.
*
* @return static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return static
* @return self

*
* @param string $uri
*
* @return static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return static
* @return self

*
* @param string $uri Named or reverse route
*
* @return static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return static
* @return self

*
* @param IncomingRequest $request
*
* @return static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return static
* @return self

@lonnieezell
Copy link
Member

I'll be honest. I'm not a fan of having both. I understand your reasoning but think it will make things more complex/confusing for the developers. I wish we could find a way to refactor the current class to work in conjunction with the url helpers.

That being said, if the rest of the team is fine with it then I'll shut up :)

@MGatner
Copy link
Member Author

MGatner commented May 5, 2021

@lonnieezell I understand. Let's think of ways we could make this better. I do think the helpers could be "beefed" up to cover this class but we would lose the ability to distinguish path parts (see my comment about "what is project" versus "what is path"). That might be an acceptable tradeoff? But there are some real issues with the current setup that need addressing. Just an example, IncomingRequest is clear that it only builds out the URI it needs for itself:

// Determine the current URI
// NOTE: This WILL NOT match the actual URL in the browser since for
// everything this cares about (and the router, etc) is the portion
// AFTER the script name. So, if hosted in a sub-folder this will
// appear different than actual URL. If you need that, use current_url().
$this->uri = $uri;
$this->detectURI($config->uriProtocol, $config->baseURL);

If you need that, use current_url().

... but then this is the entirety of current_url():

$uri = clone Services::request()->uri;

I believe that a support class (in this case URL) gives us a lot more flexibility than the helper functions for working with framework URIs - maybe we could make it an internal class? or move it out of HTTP?

@MGatner MGatner mentioned this pull request May 6, 2021
5 tasks
@MGatner MGatner closed this May 11, 2021
@MGatner MGatner mentioned this pull request Feb 16, 2023
5 tasks
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

Successfully merging this pull request may close these issues.

3 participants