Jump to content
Gadgetto

Usage of WireClassLoader in module development?

Recommended Posts

Hi there,

i am looking for a good guide for the correct use of the WireClassLoader class within a module. The Informations that can be found on this topic are very sparse. I generally have little experience with PHP namespaces and have just started using them in SnipWire.

For example, I had a look at the module "Sitemap" by Rockett:
https://gitlab.com/rockettpw/seo/markup-sitemap/-/blob/master/MarkupSitemap.module.php

Unfortunately I can't really figure it out either. Below is a short snippet from the original code. 

The questions I would like to ask:

  1. Doesn't the file need a namespace on top?
  2. In which namespace resides this class? (doesn't look like its the ProcessWire namespace?)
  3. What exactly does the addNamespace method do? Does it replace all the require_once directives?
<?php

// Require the classloaders
wire('classLoader')->addNamespace('Thepixeldeveloper\Sitemap', __DIR__ . '/src/Sitemap');
wire('classLoader')->addNamespace('Rockett\Concerns', __DIR__ . '/src/Concerns');
wire('classLoader')->addNamespace('Rockett\Support', __DIR__ . '/src/Support');

use ProcessWire\Language;
use ProcessWire\Page;
use ProcessWire\WireException;
use Rockett\Concerns;
use Rockett\Support\ParseFloat;
use Rockett\Support\ParseTimestamp;
use Thepixeldeveloper\Sitemap\Drivers\XmlWriterDriver;
use Thepixeldeveloper\Sitemap\Extensions\Link;
use Thepixeldeveloper\Sitemap\Url;
use Thepixeldeveloper\Sitemap\Urlset;

class MarkupSitemap extends WireData implements Module {
    ...
}

Maybe someone could help me to understand this?

Greetings,
Martin

Share this post


Link to post
Share on other sites

To answer part of your questions

42 minutes ago, Gadgetto said:

Doesn't the file need a namespace on top?

In the example code you've shown, they are using the use operator so they don't need the namespace on top. The use operator is mainly used for giving aliases to names of classes. 

https://www.php.net/manual/en/language.namespaces.importing.php

Share this post


Link to post
Share on other sites
3 hours ago, kongondo said:

To answer part of your questions

In the example code you've shown, they are using the use operator so they don't need the namespace on top. The use operator is mainly used for giving aliases to names of classes. 

https://www.php.net/manual/en/language.namespaces.importing.php

This would compile the file for PW 3.x or not? (seems to be an older project)

Share this post


Link to post
Share on other sites

If I understand this correctly:

  1. the classLoader is loading classes via spl_autoload_register
  2. with addNamespace you tell the classLoader which classes to load (properties: namespace + path to the class files)
  3. this makes manually including of all class files from a specific folder obsolete

What I don't understand:

wire('classLoader')->addNamespace('SnipWire\Helpers', dirname(__DIR__) . '/helpers');

Would this preload all class files from within the /helpers directory?

I still don't get the real purpose. This is probably as I also don't exactly know how the composer autoloader works...

Share this post


Link to post
Share on other sites
5 hours ago, Gadgetto said:

What exactly does the addNamespace method do?

My understanding it is useful in cases where composer is not installed, in which case classes will be loaded from file paths. 

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Gadgetto said:

Would this preload all class files from within the /helpers directory?

These two posts seems to suggest so. I haven't tried it myself. Maybe you can try and let us know 🙂

 

 

Edited by kongondo

Share this post


Link to post
Share on other sites

I use the classLoader in my TrelloWire module, you can look at the source code here. As you can see, this adds the src directory inside the module files to the WireClassLoader as the location to look for classes inside the namespace ProcessWire\TrelloWire.

Quote

Would this preload all class files from within the /helpers directory?

Almost; it doesn't preload the classes, but only on demand. This way, once I instantiate the class ProcessWire\TrelloWire\TrelloWireApi, the WireClassLoader looks for the PHP file for this class inside the src directory (the one registered for that namespace through addNamespace). If it couldn't find it, an error would be thrown. (If you're wondering why I don't use the fully qualified namespace, note the use statement at the top).

Quote

I still don't get the real purpose. This is probably as I also don't exactly know how the composer autoloader works...

Two main advantages:

  • Using an autoloader, you can map classes to directories. The exact mapping (how namespaces are resolved to file paths) is defined in the PSR-4 standard. As far as I know, the WireClassLoader loosely complies with that. Composer mostly does the same thing (although a bit more elaborate, with optimizations for production usage etc.). Basically, you have a root directory mapped to a root namespace (in this case ProcessWire\TrelloWire -> src/ directory), and from there resolve sub-namespaces to sub-directories and the class name to a file name (so ProcessWire\TrelloWire\TrelloWireApi -> src/TrelloWireApi.php). The advantage is that you add namespaces wholesale. So you only need to add the mapping for the root namespace, and can then add as many classes and nested namespaces and classes without having to worry about including any class files manually because they will be autoloaded.
  • Because class files are only included on-demand, only the classes you actually need for a request will be loaded. This of course cuts down on processing time, since otherwise you'd usually include all classes at the top for every request.

Hope this clears up some of the confusion!

  • Like 8
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, MoritzLost said:

Because class files are only included on-demand, only the classes you actually need for a request will be loaded. This of course cuts down on processing time, since otherwise you'd usually include all classes at the top for every request.

@MoritzLost: Is this true for Composer as well?

Thanks for the write-up.

Edited by kongondo

Share this post


Link to post
Share on other sites
11 hours ago, MoritzLost said:

I use the classLoader in my TrelloWire module, you can look at the source code here. As you can see, this adds the src directory inside the module files to the WireClassLoader as the location to look for classes inside the namespace ProcessWire\TrelloWire.

Almost; it doesn't preload the classes, but only on demand. This way, once I instantiate the class ProcessWire\TrelloWire\TrelloWireApi, the WireClassLoader looks for the PHP file for this class inside the src directory (the one registered for that namespace through addNamespace). If it couldn't find it, an error would be thrown. (If you're wondering why I don't use the fully qualified namespace, note the use statement at the top).

Two main advantages:

  • Using an autoloader, you can map classes to directories. The exact mapping (how namespaces are resolved to file paths) is defined in the PSR-4 standard. As far as I know, the WireClassLoader loosely complies with that. Composer mostly does the same thing (although a bit more elaborate, with optimizations for production usage etc.). Basically, you have a root directory mapped to a root namespace (in this case ProcessWire\TrelloWire -> src/ directory), and from there resolve sub-namespaces to sub-directories and the class name to a file name (so ProcessWire\TrelloWire\TrelloWireApi -> src/TrelloWireApi.php). The advantage is that you add namespaces wholesale. So you only need to add the mapping for the root namespace, and can then add as many classes and nested namespaces and classes without having to worry about including any class files manually because they will be autoloaded.
  • Because class files are only included on-demand, only the classes you actually need for a request will be loaded. This of course cuts down on processing time, since otherwise you'd usually include all classes at the top for every request.

Hope this clears up some of the confusion!

Thanks a lot @MoritzLost

Share this post


Link to post
Share on other sites
12 hours ago, MoritzLost said:

If you're wondering why I don't use the fully qualified namespace, note the use statement at the top

I see you put TrelloWire within the namespace of ProcessWire. Is there any benefit or is that just a matter of taste?

  • Like 1

Share this post


Link to post
Share on other sites
14 hours ago, kongondo said:

Is this true for Composer as well?

Yes, in general all autoloaders work this way. Composer just generates a file that registers an autoloader function through spl_autoload_register. This function is then called whenever a class is used that is not loaded yet, as a last chance to load the class before an error is thrown. Composer has a few different ways of locating the class files though. You can even generate an optimized autoloader that includes a class map which will speed up autoloading a bit.

You can also test if a class is loaded already or autoloaded with class_exists:

// using class_exists with false as the second argument, the autoloader will not be called
// this  return false for all classes that have not been manually included or autoloaded yet
var_dump(class_exists('Some\Vendor\Class', false));

// this will trigger the autoloader, so even if the previous call returned false this will return true if the class exists
var_dump(class_exists('Some\Vendor\Class'));

I have a bit more information on Composer usage in my Composer for ProcessWire tutorial 🙂

3 hours ago, Gadgetto said:

I see you put TrelloWire within the namespace of ProcessWire. Is there any benefit or is that just a matter of taste?

Personal preference! I do believe that the .module files themselves need to be inside the ProcessWire namespace in order for ProcessWire to correctly load them in all situations. But ProcessWire doesn't know about the files in the src directory, so I could use any namespace I wanted (for Composer libraries, you'd usually use the developer / organization name as the top-level namespace, so I might have used something like MoritzLost\TrelloWire). It just felt wrong to me to have the main module files inside the ProcessWire namespace and the API in an entirely different namespace, this is why I used ProcessWire\TrelloWire.

  • Like 4

Share this post


Link to post
Share on other sites

@Gadgetto

Moderator note: I have split this thread. It is advised to start new topics for questions that are not directly relevant to the existing topic. The new topic is here:

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...