Functions unable to load the $page object

If I create a function of some kind I should add it as a plugin in the plugins folder right?

I try to redirect a slash with PHP. htaccess became to complicated for me. I place this code in a plugin:

$slash_test = substr( $_SERVER['REQUEST_URI'], -1 );
if( $slash_test != '/' ) {
	header::redirect( $page->url() . '/', 301);
}

It does not work because the page object has not yet been loaded. Can this code not be added in a plugin?

I know that I could add this code first in my header snippet but is that the right way to do it?

This question does not only have to do with redirects but all code that should happen before the template has loaded that require the page object.

The variables are not set, but you can define them with their corresponding functions:

$site = site();
$pages = pages();
$page = page();

Think this should work!

Once Kiby is initialized, you ought to be able to use the page(), and site() functions to retrieve the equivalent of the equivalent of the $page and $site variables in your template. These functions are defined in the global scope, and are therefore available in all other contexts.

It does not work. Here is my new plugin code:

redirect_slash();

function redirect_slash() {
	$page = page();
	$slash_test = substr( $_SERVER['REQUEST_URI'], -1 );
	if( $slash_test != '/' ) {
		header::redirect( $page->url() . '/', 301);
	}
}

What it does is it takes the root page, not the current one.

No promises, but in the past I feel that Iā€™ve used it like thisā€¦

$site  = site();
$pages = $site->pages();

This didnā€™t work:

$pages = pages();

This worked for pages:

$site  = site();
$pages = $site->pages();

However, in my case above I need the current $page, not all pages.

How about $site->page()? Did you try that? http://getkirby.com/docs/cheatsheet/site/page

I print out the page url

$site  = site();
$page = $site->page();

echo $page->url();

When I do this inside a plugin I still got the root page, not the current one.

I guess thatā€™s simply because the plugins are loaded before the router is initialized.

Yes. It would be good to have access to the current $page on plugin init.

I tried to find a solution to it. The best way might be to try to use the build in function that create the current $page and use that function in the plugin.

Here is my very hackish solution to it

It works in some cases.

$uri = rtrim( substr( str_replace( url::base(), '', url::current() ), 1 ), '/' );
echo $pages->findByURI($uri)->slug();

It might not work on tag type of pages, or with get variables and if Kirby is in a folder I need to add it after url::base().

Hereā€™s mine (also very hackish and not reliable in all cases):

$site = site();
$site->visit(kirby()->path());
$page = $site->page();

Now I have tested your function.

Wrong in the docs, or incomplete?

In the docs it says nothing about kirby()->path(). Iā€™ve found this: $request->path() | Kirby CMS

kirby()->path();
kirby()->request()->path();

These functions seems to work exactly the same?

Where your solution work

  • Works with standard pages.
  • Works with pages with get variables (ignores them). Get variables can still be get by get(). It works like expected.

Routes donā€™t work

Routed urls fail. Is there any way to call the router to get the original url, the url that $site->visit() wants?

I tried to use this:

http://getkirby.com/docs/toolkit/api/router/run

router::run($path);

I get this error:

Strict standards: Non-static method Router::run() should not be called statically, assuming $this from incompatible context

I donā€™t know if itā€™s even the right function but at least I tried.

Update

I just created a new thread for the subject:

Not tested

I have not tried tags/categories or pagination in the url because I donā€™t have them in my project.

Are there any other cases that we should test that Iā€™ve missed?

Yeah, but not fully. They both use the same string as the base value, but kirby()->request()->path() returns a collection (you can iterate over the parts of the path string), while kirby()->path() returns the string.
You can use both as strings though, so it therefore doesnā€™t matter.

Please donā€™t even try to hack together the whole Kirby routing implementation to duplicate it in your plugin. It wonā€™t be robust enough to be reliable.

They probably wonā€™t work either.

Multi-language support is one example.


Conclusion: What I posted is a hackish solution. The true and only real solution would be to change the order of code invocation in the core so that plugins are loaded after the router has determined what page to use. That would probably break custom routes in plugins though, so it isnā€™t easy to implement at all. If you really care about this issue, please open it on GitHub. Discussing hackish solutions and their pitfalls isnā€™t worth it. :smiley:

What if plugins had a flag for delay property/option you could set. If flagged, mark plugin for load post routerā€¦ else load plugin now?

I would say itā€™s a little bit more complicated than that. When do we want to run plugins?

  • Direcly (after some Kirby functions are loaded, like it is now)
  • Just before template loads (will have $page set)
  • Just after template loads (a plugin might want to catch the output)
  • Before some custom plugin ( now we can use kirby()->plugin() )
  • After some custom plugin ( now we can use kirby()->plugin() )

Itā€™s a mix of placements in time and order, if many plugins want to run at the same time placement.