Tuesday, August 18, 2015

Pimple dependency injector container. Lambdas and Closures

As you probably saw in a previous post a give it a try on Laravel and I felt is too high level at this moment and I should learn more about the magic behind scene not just how to use a certain framework.
So I looked for a microframework hoping to be easier to understand and I discovered Silex  from the guy who made Symfony: "Silex is a PHP microframework for PHP. It is built on the shoulders of Symfony2 and Pimple and also inspired by sinatra." Next question what is Pimple?
Answer: Pimple is a small Dependency Injection Container for PHP. ( http://pimple.sensiolabs.org/ )

I have no idea about Dependency Injectors, but  found some good materials about Dependency Injection and Pimple here:

http://www.sitepoint.com/dependency-injection-with-pimple

http://fabien.potencier.org/do-you-need-a-dependency-injection-container.html

http://www.slideshare.net/fabpot/dependency-injection-in-php-5354 


Some key ideas from the Fabien's slides:


A Dependency Injector Container/ Service Container manages your SERVICES
Good rule of thumb: It manages "global" objects (objects with only one instance !=Singleton)
Example of such objects: a User, a Request, a database Connetcion, a Logger
Unlike Model objects (a Product, a blog Post)

Dependency Injection Container is simply a PHP object that manages instantiation of other objects. we call them services.


Before starting to read these articles I have to tell you that the actual implementation is based on lambdas and closures. You should have a look before on the following links to get the idea behind this PHP features:

http://culttt.com/2013/03/25/what-are-php-lambdas-and-closures/


About closure binding here:  https://www.christophh.net/2011/10/26/closure-object-binding-in-php-54/
From this article http://programmingarehard.com/2013/10/16/we-need-some-closure.html/ :

"Nowadays, it's very common to see Closures being used for containers. These containers' purpose is to create objects and inject their dependencies so we don't have to do that every single time. Consider the following example.

$container = [];

$container['EmailTemplate'] = function(){

    return new EmailTemplate;
};

$container['DataSource'] = function(){

    return new MySQLDataSource;
};

$container['NewsLetterSender'] = function() use ($container) {

    //used to created emails
    $template   = $container['EmailTemplate']();

    //used to track stats about the sending
    $dataSource = $container['DataSource']();

    return new NewsLetterSender($dataSource, $template);
};

$newsLetterSender = $container['NewsLetterSender']();

//versus the more verbose and less flexible

$newsLetterSender = new NewsLetterSender(new EmailTemplate, new MySQLDataSource);
 
As you can see, the $container's only responsibility is to create and assemble new objects. This concept is known as Inversion of Control. It's so popular because it's an elegant way to encourage dependency injection."




Sunday, August 16, 2015

Joomla componenet: Article generator from RSS feeds - part 4

 My friend who came with the request for this Joomla component gave me some feedback:

  1. - you should be able to split the article into an Intro and Fulltext as some RSS Feeds return large articles
  2.  - the photos should be saved locally (it's good for SEO)
  3. - some RSS feeds contain unwanted links, maybe do something about that also


1.    Something that I learned about articles in Joomla while researching for this task is that the content of the intro and the so called full text are stored in two different columns of the database. It is not supposed to have in Fulltext column also the intro part, only the continuation. Depending on your settings, when you click "Read more" you will be redirected to a page where you will see only the continuation or a concatenated version of Introtext and Fulltext. The configuration is done from:

System ->Global configuration ->Articles ->Show Intro Text set to Show or Hide

I added the logic in the RSSReader.php file found under /models. There is a new function called "htmlParser" which do the magic by calling some other functions:

/**
 * This function identifies images in HTML, download them locally and replace links to external images
    * with links to the local copy. Also splits article into Intro and Fulltext
    *
    * @param string $htmlInput  - string containing HTML to be parsed
    * @param integer $allow_links  - if 0 delete links, if 1 allow links
    * @return array $article_array, $article_array[0] contains introtext, $article_array[1] contains fulltext
    */

    public function htmlParser($htmlInput,$allow_links,$split_after_x)


I searched the internet for a function who is able to split the text containing HTML without breaking any tags. I found one from from CakePHP framwork text helpers via this blog: https://dodona.wordpress.com/2009/04/05/how-do-i-truncate-an-html-string-without-breaking-the-html-code/


2. For saving the photos locally I created a method called getImage which is called by htmlParser.
I am saving the images at this path:  administrator\components\com_rssaggregator\assets\images

I use pathinfo() to get information about actual image file from the link. I also add a random string to the name to be sure is not overwriting some other image with the same name.

public function getImage($link)
    {
        //assume initially that downloading operation is unsuccessfully
        $flag_success=false;
       
             
        $local_path= JPATH_BASE.DS.'administrator'.DS.'components'.DS.'com_rssaggregator'.DS.'assets'.DS.'images'.DS;
       
        //get information from path using pathinfo
        $path_parts = pathinfo($link);
       
        //if file has no extension consider default to be .gif
        if(isset($path_info['extension'])){
            $extension = '.' . substr($path_parts['extension'],0,strpos($path_parts['extension'],'?'));
        }else {
            $extension = '.gif';
        }
       
        //name of the file without extension
        $base_name = $path_parts['filename'];
       
        //local file name will be source file name + random string in order to avoid replacing an image with same name + extension
        $local_file_name= $base_name . '_' . $this->generateRandomString(10). $extension;
       
        $complete_local_path = $local_path . $local_file_name;
        $local_link='/administrator/components/com_rssaggregator/assets/images/'.$local_file_name;
       
        // test for success
        if (copy($link, $complete_local_path)) {
            $flag_success=true;
        }
           
           
        if ($flag_success===true) {
            return $local_link;
        } else {
           return false;
        }
    }



3. I concluded that is difficult to say if the links found in a RSS Feed article are good or bad so I let the Joomla admin to decide if he want to allow them as they are or replace the href value with '#'. This will make them point to the start of the article.

if ($allow_links===0) {
           
            $DOM2 = new DOMDocument;
            $DOM2->loadHTML($editedHTML);

            //get all anchors and change them to #
            $items = $DOM2->getElementsByTagName('a');
      
            foreach($items as $item){
                if($item->hasAttribute('href')) {
                    $item->setAttribute('href','#'); 
                }
            }
            //save changes made to $editedHTML
            $editedHTML=$DOM2->saveHTML();
           
        }


Fork me on GitHub!
 
I have added this project on GitHub, you can download the code from here https://github.com/cristianpana86/com_rssaggregator

Thursday, August 13, 2015

Laravel beginner tutorial. Traits and facades

Start working with Laravel:

Laravel is offering an already configured virtual machine with everything is needed to learn it, but  for some reason you may not be able to use a virtualized environment (I am in this situation). So I will install Laravel on Windows in my EasyPHP Devserver 14.1 VC11 with PHP 6.

Installing Laravel on Windows
-following on the official documentation found here: http://laravel.com/docs/5.1/installation#basic-configuration
-composer command:

    composer create-project laravel/laravel "C:\...your preffered installation path"

After this, create a virtual host which is pointing to "public" folder under root Laravel project path.
I've heard good things about video tutorials on Laracasts but I prefer a written one.I found the site learninglaravel.com which gives you access for free at most of the content (registration needed):

1. basic routing, controllers and views:  http://learninglaravel.net/laravel5/building-our-first-website
2. authentication   -  some part of the authentication tutorial si available only for paid user so I had to figure it out myself using this tutorial:

      https://laraveltips.wordpress.com/2015/06/15/how-to-make-user-login-and-registration-laravel-5-1
and official documentation at: http://laravel.com/docs/5.1/authentication#included-authenticating

Traits:

In the Authentication process  are used traits. Is the first time when I used the concept so I did a little bit of research:
From PHP manual:   As of PHP 5.4.0, PHP implements a method of code reuse called Traits. 

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is defined in a way which reduces complexity, and avoids the typical problems associated with multiple inheritance and Mixins. 

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance. 

Facades:

From http://laravel.com/docs/5.0/facades 
Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades, and you have probably been using them without even knowing it! Laravel "facades" serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.


The Laravel facades work after an intricate logic. Probably the best article I found on this subject is found here:   http://alanstorm.com/laravel_facades

Friday, August 7, 2015

Joomla componenet: Article generator from RSS feeds - part 3

This project (as many others) started just like a "..let's try it" so it suffering from the ugly title disease :) (remember myFrontController which is now a small cms which started a  test for Front Controller design pattern). So I had correct the title to "com_rssaggregator"

Now going back to the functionality, in the cron.rssaggregator.php I instantiate the RSSReader and retrieve the result into an array:

$list_of_feed_articles=$rss->getContent($model->feedsList,$model->noOfFeeds,$model->show_graphic);

What will be now saved in the database looks like this:
$tobind = array(
                           "title" => $feed_article['title'],
                           "alias" => JFilterOutput::stringURLUnicodeSlug($feed_article['title']),
                           "introtext" => $feed_article['desc'],
                           "state"=>'1',
                           "created"=>JFactory::getDate()->toSql(),
                           "featured"=>$featured,
                           "created_by"=>$author,
                           "language"=>'*',
                           "catid" => $category,
                           "metadata"=>'{"page_title":"","author":"","robots":""}',
                        );


- alias - is the slug for each article
- state - state of an article in Joomla com_content component. 1 means published, 0 unpublished
- featured - an article can be also marked as featured (that little star) in the administrator area next to article names
- created_by  - is the ID of the author
- cadid is the ID of the category in which the articles will be published.

In the RSSReader.php you can find the logic for reading the feed and parsing it according to the needs. I tried to  apply the 20/80 rules, meaning that with 20% of effort be able to read image from 80% of the RSS/Atom feeds formats.  I am checking now for the following tags:

<enclosure
<image
<media:content
<media:thumbnail

Also I added the method articleExists($slug) to the model (file models/rssaggreggator.php ). Checks the database for an article with same alias (slug), if there is one already with the same name do not added it again to the table #__content.

As this script will be executed from cron it would be nice to log all the eventual issues that the script encountered in the execution. I made a  logging errors system inspired from here:  http://joomla.stackexchange.com/questions/7286/logging-to-a-file-only-with-jlog

If the log file older than 48 hours, delete it order too prevent having large log files:

if(file_exists($filename)){
    if(time() - filectime($filename) >  48* 3600) unlink($filename);
}


Please go ahead and DOWNLOAD  this beautiful component and give it a try on your own Joomla! website. Any feedback is welcomed.

How to use it:
1.Step 1 install the component
2. In your administrator dashboard look for RSS Aggregator ->Sources view
3. Add at list one valid RSS feed
4. run cron.rssaggregator.com (if you allow direct access to your files which is very bad practice, you write in the browser the path to your file, something like this: http://127.0.0.1/joomla1/administrator/components/com_rssaggregator/cron.rssaggregator.php). Otherwise you can use a command line and execute it as any php file.
5. add the script in cron to be executed recursively, depending on how often the content of the RSS is refreshed.


Saturday, August 1, 2015

Joomla componenet: Article generator from RSS feeds - part 2

So what I want to obtain is to add from time to time articles to the database from those RSS Feeds already added in the Database.
There is a need for a script which should be scheduled after the component installation (using crontab or Windows scheduler depending on the OS)

I will place this php script under /administrator/components/com_rssagregator/ in the Joomla installation folder, and call it "cron.rssagregator.php"

At the beginning I am defining the correct JPATH_BASE from the current file location and include standard Joomla files:

    //basic to make J! happy
    define('_JEXEC', 1); //make j! happy
    define('JPATH_BASE', realpath(substr(dirname(__FILE__),0,strpos(dirname(__FILE__),'administrator'))));;
    define('DS', DIRECTORY_SEPARATOR);


    // Load up the standard stuff for testing
    require_once JPATH_BASE.DS.'includes'.DS.'defines.php';
    require_once JPATH_BASE.DS.'includes'.DS.'framework.php';

I found this information on this link: https://docs.joomla.org/Framework_Compatibility

Further an instance of JTable class is created, not before specifying the path to the specific JTable child class:
    JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_rssagregator/tables');
    $article = JTable::getInstance('content','RssagregatorTable',array());

At the specified path: '/components/com_rssagregator/tables' there is a file called content.php containing a class inheriting JTable:

     class RssagregatorTablecontent extends JTable
    {
        public function __construct($db)
        {
            parent::__construct( '#__content', 'id', $db );
        }   
    }


#__content is the name of the table where the Joomla built in component "com_content" is storing the articles.

You may want to have look on the JTable class documentation here: https://api.joomla.org/cms-3/classes/JTable.html#method_getInstance
For example the JTable::getInstance method:

        getInstance

        Static method to get an instance of a JTable class if it can be found in the table include paths. To add include paths for searching for JTable classes see JTable::addIncludePath().

        getInstance(string $type, string $prefix = 'JTable', array $config = array()) : \JTable|boolean

You can have a look using phpMyAdmin on the #__content table to see the columns names. My component will add values to some of them:

    //values to be saved in the database. this hardcoded values should be replaced with data from RSS feeds  
     $tobind = array(
                    "title" => "articol de test-3",
                   "alias" => "articol de test",
                   "introtext" => "BlueBerry Pie afadf afadfadfa faf a fadfa fad fafafdafa",
                   "state"=>'1',
                   "featured"=>'1',
                   "created_by"=>'453',
                   "language"=>'*',
                   "catid" => "2",
                   "metadata"=>'{"page_title":"","author":"","robots":""}',
                );


Using the JTable::save method the information is saved on the database:

    //if new article successfully added to database echo success message
    if ($article->save($tobind)) {
        echo "The article has been added to database. ";
    } else {
        echo "There was an error. ";
    }


Testing the script is done by directly accessing it from browser: http://joomla1/administrator/components/com_rssagregator/cron.rssagregator.php
"joomla1" is the name of my virtual host. If not using virtual host feature the link looks like:
http://127.0.0.1/joomla1/administrator/components/com_rssagregator/cron.rssagregator.php

From your Joomla administrator, under Content menu, Article manager you can see all the articles including the one newly added.


There is an issue, that even the articles are featured ("featured"=>'1') they do not appear in the list of Featured articles (menu Content -> Featured Articles ).


To solve this we need to add the ID of the new added article in the table #__content_frontpage. A new JTable child class is added in the '/components/com_rssagregator/tables' folder to handle the connection:

        class RssagregatorTablefeatureditems extends JTable
        {
            public function __construct($db)
            {
                parent::__construct( '#__content_frontpage', 'id', $db );
            }
        }


And in the cron.rssagregator.php I will add:

    $article_featured = JTable::getInstance('featureditems','RssagregatorTable',array());

    $tobind = array(
                    "content_id" => $last_id,
                  
                );
               
    if ($article_featured->save($tobind)) {
        echo "new featured article added to database. ";
    } else {
        echo "There was an error on getting a new featured article. ";
    }
   

Of course these error/successes messages are just for testing, later should be changed with some code writing on log files.

The 3 files presented in this blog post:  cron.rssagregator.php, content.php and featureditems.php are available for downloading here. They should be copied in the correct path presented previously.

Joomla componenet: Article generator from RSS feeds - part 1

     I took a break from the "myFrontController" project. A friend of mine who is using Joomla to develop some websites told me "There was a good free extension called Feedgator which
is not developed anymore. Why don't you program something like that?". Basically he needs a Joomla component able to generate articles in the database from some RSS feeds.

    I started reading about Joomla component development with the tutorial from the Joomla! official documentation: J3.x:Developing_an_MVC_Component which can be found at this link:
https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Introduction


It's a good point to start, but is not talking too much about the magic that happens behind. I found some explanations on stackoverflow:

From http://stackoverflow.com/questions/9573570/joomla-getitems-and-how-it-works
------------------------------------------------------------------------
Q:      I am looking at line 34 of /administrator/components/com_contact/views/contacts/view.html.php where is says $this->items = $this->get('Items'); What I don't understand is how that is actually calling the protected function getListQuery() on line 123 of /administrator/components/com_contact/models/contacts.php
There are also some other things I don't understand how are working... like
$this->pagination   = $this->get('Pagination');
$this->state        = $this->get('State');
What are these calling? I looked at the documentation for "get()" but it doesn't say what these are actually calling because I don't see any methods called getPagination, getState or getItems... It appears the get('Items') is somehow magically calling getListQuery().
------------------------------------------------------------------------
A:     I'm presuming 1.7/2.5+ here...
In Joomla!'s MVC the view contacts (ContactViewContacts which extends JView) automatically loads the model contacts (or in J! terminology ContactModelContacts) which as a class extends JModelList.
The get() looks in the view to get data from a registered model or a property of the view.
So:
    $this->items = $this->get('Items');
is actually a call to the model ContactModelContacts which has a matching getItems() in it's parent.
The model file com_contact/models/contacts.php doesn't implement it's own getItems(), so the getItems() from the JModelList class is used (found in /libraries/joomla/application/component/modellist.php).
This in turn calls getListQuery() - no magic just inheritance.
The $this->get('Pagination') is doing the same thing, ie. accessing the implementation in the models parent.
The $this->get('State') is probably going all the way back to the JModel implementation.
------------------------------------------------------------------------

Some other links from where I tried to understand how the Joomla component works:





  
       In order to accelerate the process and create all the files structure I used the free service from: http://www.component-creator.com (I would not called it  cheating, rather increasing productivity)
and generated a Joomla component. This component has just some backend logic and one DB table. There is a view from where you can enter  RSS feed linka, name and  number of posts.

To this structure I added in the "/site" part of the component, the logic for reading the RSS feeds and just display them.

I added a simple view which contains a display() method  :

/com_rssagregator/site/views/rssagregator/view.html.php

            function display($tpl = null)
            {
                // Assign data to the view
                $this->msg = $this->get('Msg');
               
                // Check for errors.
                if (count($errors = $this->get('Errors')))
                {
                    JLog::add(implode('<br />', $errors), JLog::WARNING, 'jerror');
       
                    return false;
                }
       
                // Display the view
                parent::display($tpl);
            }


In the corresponded model class I implemented the getMsg() method.
 /com_rssagregator/site/models/rssagregator.php.
The logic is :
1. read the list of feeds from the database (which were previously added in back end - administrator view)
2. read the the number of posts to be displayed for each feed from the database (which also were previously added in back end - administrator view)
3. require_once the "RSSReader.php" file in which I implemented the logic for reading feed posts
4. call getContent($feedsList,$noOfFeeds);
5. in the RSSReader.php each feed is read entirely and then only the first "noOfPosts" are returned in the output. The output is formatted and ready to be sent to the view for displaying it.

    public function parseData($feedObj,$limit)
    {
   
        $rss = new DOMDocument();
        $rss->loadXML($feedObj);
       
        $feed = array();
        foreach ($rss->getElementsByTagName('item') as $node) {
            $item = array (
                'title' => $node->getElementsByTagName('title')->item(0)->nodeValue,
                'desc' => $node->getElementsByTagName('description')->item(0)->nodeValue,
                'link' => $node->getElementsByTagName('link')->item(0)->nodeValue,
                'date' => $node->getElementsByTagName('pubDate')->item(0)->nodeValue,
                );
            array_push($feed, $item);
        }
       
        $output='';
        for($x=0;$x<$limit;$x++) {
            $title = str_replace(' & ', ' &amp; ', $feed[$x]['title']);
            $link = $feed[$x]['link'];
            $description = $feed[$x]['desc'];
            $date = date('l F d, Y', strtotime($feed[$x]['date']));
            ///////////////////////////////////////////////////////////
           
            $output.= '<p><strong><a href="'.$link.'" title="'.$title.'">'.$title.'</a></strong><br />';
            $output.= '<small><em>Posted on '.$date.'</em></small></p>';
            $output.= '<p>'.$description.'</p><br/><br/>';
           
           
        }
        return $output;
    }

6. In order to visualize the result, from backend add a menu item pointing to this newly created view. This is possible only if
in the "view/<view_name>/tmpl/" exists a an xml file contianing a layout title and description like this one:

    <?xml version="1.0" encoding="utf-8"?>
    <metadata>
        <layout title="COM_RSSAGREGATOR_CPANARSS_VIEW_DEFAULT">
            <message>link menu catre RSSAGREGATOR default view</message>
        </layout>
    </metadata>

   
The result of my work can be downloaded from here.

Instead of directly displaying the feed posts this  component should be further developed to add the feed posts to the database as articles.