We are excited to announce that the IIS.NET Forums are moving to the new Microsoft Q&A experience. Learn more >

Question about implementation of WinCache beyond basic installRSS

25 replies

Last post Jul 17, 2013 01:11 PM by MuscleJunkie

  • Question about implementation of WinCache beyond basic install

    Sep 28, 2011 03:31 PM|MuscleJunkie|LINK

     We have recently starting using WinCache and NTS PHP 5.2.17 for one of our very large sites.  Other than the basic installation, I'm trying to understand how this extension can be used further.

    •  Is there any sort of documentation for  use with PHPBB2?
    • Regarding the usercache, can someone clarify if it can be used for such a thing

    Example:  Say a PHP page has the following query

     

     $sql = "SELECT f.forum_id, f.cat_id, f.forum_name, f.forum_desc, f.forum_status, f.forum_order, f.forum_posts, f.forum_last_post_id, f.forum_last_post_time, f.auth_view, f.forum_icon, f.forum_parent
                    FROM ( " . FORUMS_TABLE . " f)
                    
                    ORDER BY f.cat_id, f.forum_order";

            if ( !($result = $db->sql_query($sql)) )
            {
                message_die(GENERAL_ERROR, 'Could not obtain forums information', '', __LINE__, __FILE__, $sql);
            }


          $forum_data = $db->sql_fetchrowset($result);

    Could the results of this query as $forum_data be added to the usercache?  If so, how could they be retrieved and be made available on future page loads?  We currently have the ability to write the result (it can be rather large) to a file (serialized) and read the file for the $forum_data on future page loads.

     Thank You

  • Re: Question about implementation of WinCache beyond basic install

    Sep 29, 2011 12:33 AM|laurin1|LINK

    Sure. This is generally how we use it:

    $bWinCache = false;

    $forum_data = wincache_ucache_get("FORUMDATAKEY", $bWinCache);

    if(!$bWinCache){

    $sql = "SELECT f.forum_id, f.cat_id, f.forum_name, f.forum_desc, f.forum_status, f.forum_order, f.forum_posts, f.forum_last_post_id, f.forum_last_post_time, f.auth_view, f.forum_icon, f.forum_parent
    FROM ( " . FORUMS_TABLE . " f)

    ORDER BY f.cat_id, f.forum_order";

    if ( !($result = $db->sql_query($sql)) )
    {
    message_die(GENERAL_ERROR, 'Could not obtain forums information', '', __LINE__, __FILE__, $sql);
    }


    $forum_data = $db->sql_fetchrowset($result);

    wincache_ucache_set("FORUMDATAKEY", $forum_data, 600);

     }

  • Re: Question about implementation of WinCache beyond basic install

    Sep 29, 2011 03:09 AM|MuscleJunkie|LINK

     Wow - thank you very much for the quick response.

     So as you can see, this particular query contains some fairly static info that doesn't change to much and isn't user specific as php later on takes the $forumdata and gives out what is specific to the particular visitor to the page.

    I think I'll have a few more questions but they will have to wait until the AM.  

     

    Thank You very much again!

  • Re: Question about implementation of WinCache beyond basic install

    Sep 29, 2011 04:48 PM|MuscleJunkie|LINK

     Ok a few questions - 1st on the initial query.  Seems as if I need to make an adjustment.

    $sql = "SELECT  f.*
                    FROM ( " . FORUMS_TABLE . " f)
                   
                    ORDER BY f.cat_id, f.forum_order";

        if ( !($result = $db->sql_query($sql)) )
        {
            message_die(GENERAL_ERROR, 'Could not query forums information', '', __LINE__, __FILE__, $sql);
        }

        $forum_data = array();
        while( $row = $db->sql_fetchrow($result) )
        {
            $forum_data[] = $row;

    if ($row['forum_last_post_time'] > $userdata['user_lastvisit'])
       
        $new_topic_data[$row['forum_id']][$row['topic_id']] = $row['forum_last_post_time'];
        }


       
        $db->sql_freeresult($result);

        if ( !($total_forums = count($forum_data)) )
        {
            message_die(GENERAL_MESSAGE, $lang['No_forums']);
        }


    How will the wcache implementation need to be changed for this?

     

    Why going over some of the more main queries of the platform, I came across these that we are hoping to implement use of the User Cache with (if appropriate)

    • From the topic page (Pull data for topic - I imagine the key will need to be specific for each topic as well as the $start variable)
     $sql = "SELECT u.username, u.user_id, u.user_posts, u.user_from, u.user_email, u.user_regdate, u.user_viewemail, u.user_rank, u.user_sig, u.user_sig_bbcode_uid, u.user_avatar, u.user_avatar_type, u.user_allowavatar, u.user_allowsmile, u.user_level, p.*,  pt.post_text, pt.post_subject, pt.bbcode_uid
            FROM " . POSTS_TABLE . " p, " . USERS_TABLE . " u, " . POSTS_TEXT_TABLE . " pt
            WHERE p.topic_id = $topic_id
                    AND pt.post_id = p.post_id
                    AND u.user_id = p.poster_id
            ORDER BY p.post_time $post_time_order
            LIMIT $start, ".$board_config['posts_per_page'];
    if ( !($result = $db->sql_query($sql)) )
    {
            message_die(GENERAL_ERROR, "Could not obtain post/user information.", '', __LINE__, __FILE__, $sql);
    }

    $postrow = array();
    if ($row = $db->sql_fetchrow($result))
    {
            do
            {
                    $postrow[] = $row;
            }
            while ($row = $db->sql_fetchrow($result));
            $db->sql_freeresult($result);

            $total_posts = count($postrow);
    }
    else
    {
       include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
       sync('topic', $topic_id);

       message_die(GENERAL_MESSAGE, $lang['No_posts_topic']);
    }


    Thanks

     

     

     

  • Re: Question about implementation of WinCache beyond basic install

    Sep 30, 2011 04:13 PM|laurin1|LINK

    It's kind of hard to tell without the context of your code. You can make it work. However, you can't store a resource in Wincache, it has to be the actual data (like your first example), as an array or as a seralized object.
  • Re: Question about implementation of WinCache beyond basic install

    Sep 30, 2011 04:54 PM|MuscleJunkie|LINK

     Thank You once again for the response.

     

    Ok, so the 1st code example we provided

     

      $sql = "SELECT f.forum_id, f.cat_id, f.forum_name, f.forum_desc, f.forum_status, f.forum_order, f.forum_posts, f.forum_last_post_id, f.forum_last_post_time, f.auth_view, f.forum_icon, f.forum_parent
                    FROM ( " . FORUMS_TABLE . " f)
                    
                    ORDER BY f.cat_id, f.forum_order";

            if ( !($result = $db->sql_query($sql)) )
            {
                message_die(GENERAL_ERROR, 'Could not obtain forums information', '', __LINE__, __FILE__, $sql);
            }


          $forum_data = $db->sql_fetchrowset($result);

     

     

    This was fine with the exception of one thing.  After the query is ran, there is a portion of code (from the revised 2nd post)

     $forum_data = array();
        while( $row = $db->sql_fetchrow($result) )
        {
            $forum_data[] = $row;

     

     if ($row['forum_last_post_time'] > $userdata['user_lastvisit'])
       
        $new_topic_data[$row['forum_id']][$row['topic_id']] = $row['forum_last_post_time'];
        }


       
        $db->sql_freeresult($result);

        if ( !($total_forums = count($forum_data)) )
        {
            message_die(GENERAL_MESSAGE, $lang['No_forums']);
        }

     

    that is supposed to take the results from the query, make them available as $forum_data but "also" look at the 'forum_last_post_time" for each row that was returned and compare it with and existing item ($userdata['user_lastvisit']) and if there have been posts made since the user last visited it will set the make available in $new_topic_data the rows ('forum_id'} that have the new posts.  Later on the $new_topic_data is used to highlight the sections that have had new posts since the user last visited.  By using the initial WINCACHE example you provided, everything works with the except of the $new_topic_data

     

    This is the code as it exists (and works) before wincache.  So just trying to figure out how to rewrite this properly to achieve the same result.

    $sql = "SELECT  f.*
                    FROM ( " . FORUMS_TABLE . " f)
                   
                    ORDER BY f.cat_id, f.forum_order";

        if ( !($result = $db->sql_query($sql)) )
        {
            message_die(GENERAL_ERROR, 'Could not query forums information', '', __LINE__, __FILE__, $sql);
        }

        $forum_data = array();
        while( $row = $db->sql_fetchrow($result) )
        {
            $forum_data[] = $row;

    if ($row['forum_last_post_time'] > $userdata['user_lastvisit'])
       
        $new_topic_data[$row['forum_id']][$row['topic_id']] = $row['forum_last_post_time'];
        }


       
        $db->sql_freeresult($result);

        if ( !($total_forums = count($forum_data)) )
        {
            message_die(GENERAL_MESSAGE, $lang['No_forums']);
        }

  • Re: Question about implementation of WinCache beyond basic install

    Oct 06, 2011 09:47 AM|laurin1|LINK

    I guess I'm confused. Can you give me the complete code or a larger chunk? Where are you pulling the cached version of $forum_data out?
  • Re: Question about implementation of WinCache beyond basic install

    Oct 11, 2011 06:18 PM|MuscleJunkie|LINK

     Sure no problem.  Here is a larger chunk of the code

     $sql = "SELECT c.cat_id, c.cat_title, c.cat_order
        FROM " . CATEGORIES_TABLE . " c
        ORDER BY c.cat_order";
    if( !($result = $db->sql_query($sql)) )
    {
        message_die(GENERAL_ERROR, 'Could not query categories list', '', __LINE__, __FILE__, $sql);
    }

    $category_rows = array();
    while( $category_rows[] = $db->sql_fetchrow($result) );
    $db->sql_freeresult($result);

    $subforums_list = array();

    if( ( $total_categories = count($category_rows) ) )
    {

    $sql = "SELECT  f.*
                    FROM ( " . FORUMS_TABLE . " f)
                   
                    ORDER BY f.cat_id, f.forum_order";

        if ( !($result = $db->sql_query($sql)) )
        {
            message_die(GENERAL_ERROR, 'Could not query forums information', '', __LINE__, __FILE__, $sql);
        }

        $forum_data = array();
        while( $row = $db->sql_fetchrow($result) )
        {
            $forum_data[] = $row;

    if ($row['forum_last_post_time'] > $userdata['user_lastvisit'])
       
        $new_topic_data[$row['forum_id']][$row['topic_id']] = $row['forum_last_post_time'];
        }
       
        $db->sql_freeresult($result);

        if ( !($total_forums = count($forum_data)) )
        {
            message_die(GENERAL_MESSAGE, $lang['No_forums']);
        }


    If you need more, please shoot me an email address and I'll fire the file over.

     

  • Re: Question about implementation of WinCache beyond basic install

    Oct 14, 2011 10:03 AM|laurin1|LINK

    Effective caching means determing what portions of your code are served better my data stored in RAM, versus queried, so this is a bit of a guess....

     _______________________________________________________________

    35      $sql = "SELECT c.cat_id, c.cat_title, c.cat_order
    36                FROM ".CATEGORIES_TABLE." c
    37                ORDER BY c.cat_order";
    38 
    39 	if(!($result = $db->sql_query($sql))){
    40 
    41 		message_die(GENERAL_ERROR, 'Could not query categories list', '', __LINE__, __FILE__, $sql);
    42 
    43 	}
    44 
    45 	$category_rows = array();
    46 
    47 	while(($category_rows[] = $db->sql_fetchrow($result))) ;
    48 
    49 	$db->sql_freeresult($result);
    50 
    51 	$bWinCache = false;
    52 
    53 	$forum_data = wincache_ucache_get("FORUMDATAKEY", $bWinCache);
    54 
    55 	if(!$bWinCache){
    56 
    57 		$sql = "SELECT f.forum_id, f.cat_id, f.forum_name, f.forum_desc, f.forum_status, f.forum_order,
    58 					f.forum_posts, f.forum_last_post_id, f.forum_last_post_time, f.auth_view, f.forum_icon,
    59 					f.forum_parent
    60 				FROM ( ".FORUMS_TABLE." f)
    61                     ORDER BY f.cat_id, f.forum_order";
    62 
    63 		if(!($result = $db->sql_query($sql))){
    64 			message_die(GENERAL_ERROR, 'Could not obtain forums information', '', __LINE__, __FILE__,
    65 			            $sql);
    66 		}
    67 
    68 		$forum_data = $db->sql_fetchrowset($result);
    69 
    70 		wincache_ucache_set("FORUMDATAKEY", $forum_data, 600);
    71 
    72 	}
    73 
    74 	$subforums_list = array();
    75 
    76 	if(($total_categories = count($category_rows))){
    77 
    78 		foreach($forum_data as $row){
    79 
    80 			if($row['forum_last_post_time'] > $userdata['user_lastvisit'])
    81 				$new_topic_data[$row['forum_id']][$row['topic_id']] = $row['forum_last_post_time'];
    82 
    83 		}
    84 
    85 		$db->sql_freeresult($result);
    86 
    87 		if(!($total_forums = count($forum_data))){
    88 
    89 			message_die(GENERAL_MESSAGE, $lang['No_forums']);
    90 
    91 		}
    92 
    93 	}
  • Re: Question about implementation of WinCache beyond basic install

    Oct 17, 2011 07:20 PM|MuscleJunkie|LINK

     Hi and Thank You for this.  It seems to work just fine.  The 2nd query has a large result so I think being served out of the cache is a good way to go.

     

    Seems where I was confused is where you had the $subforums_list = array(); after  the Wincache section and for some reason I had it before.  THat is why it wasn't working for me when I tested after 1st suggestion in response to the OP.

     

    Do you have a way I could contact you regarding a few other questions.  After we have a chance to talk, I'd like to come back to this thread to summarize.  I am hoping to take what is gained from this to provide to the community our platform is based on some options for others to effectively use WinCache.  The newer version of our platform apparently has this built in but some of the largest users are still using the previous version as we are since it offers a great deal more flexibility and overall better performance and scalability especially at the levels we are operating.  With WinCache implementation it appears this is even more the case.

     

    Thanks in advance.

     

     

     

     

  • Re: Question about implementation of WinCache beyond basic install

    Dec 24, 2011 08:54 AM|laurin1|LINK

    I just saw your last post. Never got an email for it. Did you get it figured out?

  • Re: Question about implementation of WinCache beyond basic install

    Feb 01, 2012 02:54 PM|MuscleJunkie|LINK

    Hi, No, was waiting for a reply and then things put on hold due to the holidays, etc. As I mentioned in my previous response Do you have a way I could contact you regarding a few other questions. After we have a chance to talk, I'd like to come back to this thread to summarize. I am hoping to take what is gained from this to provide to the community our platform is based on some options for others to effectively use WinCache. The newer version of our platform apparently has this built in but some of the largest users are still using the previous version as we are since it offers a great deal more flexibility and overall better performance and scalability especially at the levels we are operating. With WinCache implementation it appears this is even more the case. Thanks in advance.
  • Re: Question about implementation of WinCache beyond basic install

    Feb 01, 2012 04:39 PM|laurin1|LINK

    I'm pretty slammed right now. I'll try to answer what I can via the forum right now, and if we still need a phone call, we can plan that down the road.

  • Re: Question about implementation of WinCache beyond basic install

    May 12, 2012 09:53 AM|MuscleJunkie|LINK

    Had another WinCache implementation question.

    This query

    *******

    $sql = "SELECT *
    FROM " . CONFIG_TABLE;
    if( !($result = $db->sql_query($sql)) )
    {
    message_die(CRITICAL_ERROR, "Could not query config information", "", __LINE__, __FILE__, $sql);
    }

    while ( $row = $db->sql_fetchrow($result) )
    {
    $board_config[$row['config_name']] = $row['config_value'];
    }

    ********
    How should it be written to store this in Wincache?

    Thanks
  • Re: Question about implementation of WinCache beyond basic install

    May 14, 2012 02:19 PM|laurin1|LINK

    I would just store the $board_config array in wincache. I use a standard format like this:

    function getBoardConfig(){
    
    	$bWinCacheGet = false;
    	$sWinCacheKey = __FUNCTION__;
    	$aReturn      = wincache_ucache_get($sWinCacheKey, $bWinCacheGet);
    
    	if(!$bWinCacheGet){
    
    		$sql = "SELECT *
    	          FROM ".CONFIG_TABLE;
    
    		if(!($result = $db->sql_query($sql))){
    			message_die(CRITICAL_ERROR, "Could not query config information", "", __LINE__, __FILE__,
    			            $sql);
    		}
    
    		while($row = $db->sql_fetchrow($result)){
    			$board_config[$row['config_name']] = $row['config_value'];
    		}
    
    		$aReturn = $board_config;
    
    		wincache_ucache_set($sWinCacheKey, $aReturn, TTL_VALUE);
    
    	}
    
    	return $aReturn;
    
    }
    
  • Re: Question about implementation of WinCache beyond basic install

    May 16, 2012 11:39 AM|MuscleJunkie|LINK

    So using something like this would mean having to change all places to call this function instead of using the current method? If so, what could be changed to use this with the current method? Thanks
  • Re: Question about implementation of WinCache beyond basic install

    May 16, 2012 11:42 AM|laurin1|LINK

    I'm not sure what you mean. You can create a common function for storing and restrieving this type of data. Yes, we use this type of code throughout our site.
  • Re: Question about implementation of WinCache beyond basic install

    May 16, 2012 11:59 AM|MuscleJunkie|LINK

    Well in V2 of the PHP BB software - the board config is pulled at the "common" php file. How this current works I'll use viewtopic as an example

    At the start of viewtopic, there is an include of the common php file at the very beginning of view topic


    In common, then you have this code

    $sql = "SELECT *
    FROM " . CONFIG_TABLE;
    if( !($result = $db->sql_query($sql)) )
    {
    message_die(CRITICAL_ERROR, "Could not query config information", "", __LINE__, __FILE__, $sql);
    }

    while ( $row = $db->sql_fetchrow($result) )
    {
    $board_config[$row['config_name']] = $row['config_value'];
    }

    So now back to the example at viewtopic


    Anytime a board config is needed - it is now available to be used at any point in view topic or any other file that has the include for the common php file at the beginning.

    So, what I was trying to clarify is, if we go with setting a function for this instead of using the current method (having the include of the common php file) that would mean changing each file that has this include right?

    Instead, was hoping to change the least amount of files possible and maybe just changing the code I posted above from the common php file to use wincache and everything else would stay the same.....

  • Re: Question about implementation of WinCache beyond basic install

    May 16, 2012 12:08 PM|laurin1|LINK

    I don't know enough about your code, but what I am saying is yes, change it in your common.php file.
  • Re: Question about implementation of WinCache beyond basic install

    May 16, 2012 12:52 PM|MuscleJunkie|LINK

    Sorry, here I'll give you an example

    An example page would be this

    A user visits the whos online php page on the site

    At the beginning of that PHP file there is this code

    include($phpbb_root_path . 'common.'.$phpEx);

    So since this is in the beginning, that means that all the code in the common php file will be ran and then the code on whos online will continue

    So we go to the common php file. There are a couple sections in the code of this file that that deal with what we are writing here about

    The 1st is here

    //
    // Define some basic configuration arrays this also prevents
    // malicious rewriting of language and otherarray values via
    // URI params
    //
    $board_config = array();


    Then further down we have

    // Setup forum wide options, if this fails
    // then we output a CRITICAL_ERROR since
    // basic forum information is not available
    //
    $sql = "SELECT *
    FROM " . CONFIG_TABLE;
    if( !($result = $db->sql_query($sql)) )
    {
    message_die(CRITICAL_ERROR, "Could not query config information", "", __LINE__, __FILE__, $sql);
    }

    while ( $row = $db->sql_fetchrow($result) )
    {
    $board_config[$row['config_name']] = $row['config_value'];
    }


    So with this current code, the board configs should be available to the whos online php file if they are requested as $board_config['XXXXXXXX']

    So we now go back to the whos online php file and there is code where things from the board config are requested to set for the template

    $template->assign_block_vars("$which_row", array(
    'ROW_COLOR' => '#' . $row_color,
    'ROW_CLASS' => $row_class,
    'USERNAME' => $username,
    'LASTUPDATE' => create_date($board_config['default_dateformat'], $row['session_time'], $board_config['board_timezone']),
    'FORUM_LOCATION' => $location,
    'U_USER_PROFILE' => append_sid("profile.$phpEx?mode=viewprofile&" . POST_USERS_URL . '=' . $user_id),
    'U_FORUM_LOCATION' => append_sid($location_url))
    );



    This is how the "current method" operates. We are hoping to not have to change any code in any file such as the example from the whos online php file above but only make a change to the code in the common php file to use wincache.


    So now just trying to determine what changes need to be made in the two places in the common php file we mentioned above that will allow use of wincache for the board_config without having the make any other changes to files such as our whos online php file example above. So now we go back to the whos online php file.
  • Re: Question about implementation of WinCache beyond basic install

    May 17, 2012 04:48 PM|laurin1|LINK

    The code I gave you should work fine. You don't have to put in a function. The first time the page loads, it will load $board_config using SQL and store that in Wincache. Subsequent times, it will load $board_config from Wincache, so that the SQL table isn't hit, until the TTL expires (if you set one.)

  • Re: Question about implementation of WinCache beyond basic install

    May 23, 2012 12:28 PM|MuscleJunkie|LINK

     What changes should be made to what you provided since we are not using it as a function?

     

     

  • Re: Question about implementation of WinCache beyond basic install

    May 24, 2012 04:48 PM|laurin1|LINK

     3 $bWinCacheGet = false;
     4 $sWinCacheKey = __FUNCTION__;
     5 $board_config = wincache_ucache_get($sWinCacheKey, $bWinCacheGet);
     6 
     7 if(!$bWinCacheGet){
     8 
     9      $sql = "SELECT *
    10                FROM ".CONFIG_TABLE;
    11 
    12      if(!($result = $db->sql_query($sql))){
    13           message_die(CRITICAL_ERROR, "Could not query config information", "", __LINE__, __FILE__,
    14                       $sql);
    15      }
    16 
    17      while($row = $db->sql_fetchrow($result)){
    18           $board_config[$row['config_name']] = $row['config_value'];
    19      }
    20 
    21      wincache_ucache_set($sWinCacheKey, $board_config, TTL_VALUE);
    22 
    23 }
  • Re: Question about implementation of WinCache beyond basic install

    Jun 01, 2012 10:53 PM|MuscleJunkie|LINK

     Perfect, that worked.  Just had to make a few minor changes

    Thank You!!

    Another big query we are trying to implement WINCACHE with is from the topic page.  The code goes like this

     

    $sql = "SELECT SQL_CACHE u.username, u.user_id, u.user_posts, u.user_from, u.user_email, u.user_regdate, u.user_viewemail, u.user_rank, u.user_sig, u.user_sig_bbcode_uid, u.user_avatar, u.user_avatar_type, u.user_allowavatar, u.user_allowsmile, u.user_level, p.*,  pt.post_text, pt.post_subject, pt.bbcode_uid
            FROM " . POSTS_TABLE . " p, " . USERS_TABLE . " u, " . POSTS_TEXT_TABLE . " pt
            WHERE p.topic_id = $topic_id               
                    AND pt.post_id = p.post_id
                    AND u.user_id = p.poster_id
            ORDER BY p.post_time $post_time_order
            LIMIT $start, ".$board_config['posts_per_page'];

    if ( !($result = $db->sql_query($sql)) )
    {
            message_die(GENERAL_ERROR, "Could not obtain post/user information.", '', __LINE__, __FILE__, $sql);

    $postrow = array();
    if ($row = $db->sql_fetchrow($result))
    {
            do
            {
                    $postrow[] = $row;
            }
            while ($row = $db->sql_fetchrow($result));
            $db->sql_freeresult($result);

            $total_posts = count($postrow);
    }
    else
    {
       include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
       sync('topic', $topic_id);

       message_die(GENERAL_MESSAGE, $lang['No_posts_topic']);
    }



    $resync = FALSE;
    if ($forum_topic_data['topic_replies'] + 1 < $start + count($postrow))
    {
       $resync = TRUE;
    }
    elseif ($start + $board_config['posts_per_page'] > $forum_topic_data['topic_replies'])
    {
       $row_id = intval($forum_topic_data['topic_replies']) % intval($board_config['posts_per_page']);
       if ($postrow[$row_id]['post_id'] != $forum_topic_data['topic_last_post_id'] || $start + count($postrow) < $forum_topic_data['topic_replies'])
       {
          $resync = TRUE;
       }
    }
    elseif (count($postrow) < $board_config['posts_per_page'])
    {
       $resync = TRUE;
    }

    if ($resync)
    {
       include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
       sync('topic', $topic_id);

       $result = $db->sql_query('SELECT COUNT(post_id) AS total FROM ' . POSTS_TABLE . ' WHERE topic_id = ' . $topic_id);
       $row = $db->sql_fetchrow($result);
       $total_replies = $row['total'];
    }

     

    What would be the best method to implement Wincache for this section.  Prior to exploring Wincache implementation, we changed the 1st part of the code to this which has resulted in marginal improvement

     $p_array = array();
    $sql = "SELECT p.post_id FROM " . POSTS_TABLE . " p
         WHERE p.topic_id = $topic_id
        LIMIT $start, " . $board_config['posts_per_page'];

    if ( !($result = $db->sql_query($sql)) )
    {
       message_die(GENERAL_ERROR, "Could not obtain post index information.", '', __LINE__, __FILE__, $sql);
    }

    while ($row = $db->sql_fetchrow($result))
    {
        $p_array[] = $row['post_id'];
    }


    $post_index = implode(",",$p_array);

    $sql = "SELECT u.username, u.user_id, u.user_posts, u.user_from, u.user_email, u.user_regdate, u.user_viewemail, u.user_rank, u.user_sig, u.user_sig_bbcode_uid, u.user_avatar, u.user_avatar_type, u.user_allowavatar, u.user_allowsmile, u.user_level, p.*,  pt.post_text, pt.post_subject, pt.bbcode_uid
       FROM " . POSTS_TABLE . " p, " . USERS_TABLE . " u, " . POSTS_TEXT_TABLE . " pt
       WHERE p.post_id in ($post_index)
          AND pt.post_id = p.post_id
          AND u.user_id = p.poster_id
       ORDER BY p.post_time $post_time_order";
    if ( !($result = $db->sql_query($sql)) )
    {
            message_die(GENERAL_ERROR, "Could not obtain post/user information.", '', __LINE__, __FILE__, $sql);
    }

     

    So also not sure which to use as the basis for a Wincache implementation as well.

     

    Thanks again in advance.

     

     

     

     

     

  • Re: Question about implementation of WinCache beyond basic install

    Jun 06, 2012 10:27 AM|laurin1|LINK

    This one is really hard to do out context. How often do you expect the $postrow array data to change?
  • Re: Question about implementation of WinCache beyond basic install

    Jul 17, 2013 01:11 PM|MuscleJunkie|LINK

    Hi again - its been awhile since I've written on this thread.  I've worked out alot regarding further implementation of WinCache and it seems to be working great.  Its a bit disappointing that there seems to be limits of how much RAM you can dedicate to the various caches.  What is the reason for this?

    Anyway - on to my next question.  This is regarding the sessions cache.  We are looking to implement it but not quite sure on the best method.  Currently session for the application are stored in a MYSQL HEAP table and we are thinking that utilizing the session cache of WINCACHE might be a better option?

    Here is the code that is currently used (more or less) for sessions.

    function session_begin($user_id, $user_ip, $page_id, $auto_create = 0, $enable_autologin = 0, $admin = 0)
    {
    	global $db, $board_config;
    	global $HTTP_COOKIE_VARS, $HTTP_GET_VARS, $SID;
    
    	$cookiename = $board_config['cookie_name'];
    	$cookiepath = $board_config['cookie_path'];
    	$cookiedomain = $board_config['cookie_domain'];
    	$cookiesecure = $board_config['cookie_secure'];
    
    	if ( isset($HTTP_COOKIE_VARS[$cookiename . '_sid']) || isset($HTTP_COOKIE_VARS[$cookiename . '_data']) )
    	{
    		$session_id = isset($HTTP_COOKIE_VARS[$cookiename . '_sid']) ? $HTTP_COOKIE_VARS[$cookiename . '_sid'] : '';
    		$sessiondata = isset($HTTP_COOKIE_VARS[$cookiename . '_data']) ? unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data'])) : array();
    		$sessionmethod = SESSION_METHOD_COOKIE;
    	}
    	else
    	{
    		$sessiondata = array();
    		$session_id = ( isset($HTTP_GET_VARS['sid']) ) ? $HTTP_GET_VARS['sid'] : '';
    		$sessionmethod = SESSION_METHOD_GET;
    	}
    
    	//
    	if (!preg_match('/^[A-Za-z0-9]*$/', $session_id))
    	{
    		$session_id = '';
    	}
    
    	$page_id = (int) $page_id;
    
    	$last_visit = 0;
    	$current_time = time();
    
    	//
    	// Are auto-logins allowed?
    	// If allow_autologin is not set or is true then they are
    	// (same behaviour as old 2.0.x session code)
    	//
    	if (isset($board_config['allow_autologin']) && !$board_config['allow_autologin'])
    	{
    		$enable_autologin = $sessiondata['autologinid'] = false;
    	}
    
    	//
    	// First off attempt to join with the autologin value if we have one
    	// If not, just use the user_id value
    	//
    	$userdata = array();
    
    	if ($user_id != ANONYMOUS)
    	{
    		if (isset($sessiondata['autologinid']) && (string) $sessiondata['autologinid'] != '' && $user_id)
    		{
    			$sql = 'SELECT u.*
    				FROM ' . USERS_TABLE . ' u, ' . SESSIONS_KEYS_TABLE . ' k
    				WHERE u.user_id = ' . (int) $user_id . "
    					AND u.user_active = 1
    					AND k.user_id = u.user_id
    					AND k.key_id = '" . md5($sessiondata['autologinid']) . "'";
    			if (!($result = $db->sql_query($sql)))
    			{
    				message_die(CRITICAL_ERROR, 'Error doing DB query userdata row fetch', '', __LINE__, __FILE__, $sql);
    			}
    
    			$userdata = $db->sql_fetchrow($result);
    			$db->sql_freeresult($result);
    		
    			$enable_autologin = $login = 1;
    		}
    		else if (!$auto_create)
    		{
    			$sessiondata['autologinid'] = '';
    			$sessiondata['userid'] = $user_id;
    
    			$sql = 'SELECT *
    				FROM ' . USERS_TABLE . '
    				WHERE user_id = ' . (int) $user_id . '
    					AND user_active = 1';
    			if (!($result = $db->sql_query($sql)))
    			{
    				message_die(CRITICAL_ERROR, 'Error doing DB query userdata row fetch', '', __LINE__, __FILE__, $sql);
    			}
    
    			$userdata = $db->sql_fetchrow($result);
    			$db->sql_freeresult($result);
    
    			$login = 1;
    		}
    	}
    
    	//
    	// At this point either $userdata should be populated or
    	// one of the below is true
    	// * Key didn't match one in the DB
    	// * User does not exist
    	// * User is inactive
    	//
    	if (!sizeof($userdata) || !is_array($userdata) || !$userdata)
    	{
    		$sessiondata['autologinid'] = '';
    		$sessiondata['userid'] = $user_id = ANONYMOUS;
    		$enable_autologin = $login = 0;
    
    		$sql = 'SELECT *
    			FROM ' . USERS_TABLE . '
    			WHERE user_id = ' . (int) $user_id;
    		if (!($result = $db->sql_query($sql)))
    		{
    			message_die(CRITICAL_ERROR, 'Error doing DB query userdata row fetch', '', __LINE__, __FILE__, $sql);
    		}
    
    		$userdata = $db->sql_fetchrow($result);
    		$db->sql_freeresult($result);
    	}
    
    
    	//
    	// Initial ban check against user id, IP and email address
    	//
    	preg_match('/(..)(..)(..)(..)/', $user_ip, $user_ip_parts);
    
    	$sql = "SELECT ban_ip, ban_userid, ban_email
    		FROM " . BANLIST_TABLE . "
    		WHERE ban_ip IN ('" . $user_ip_parts[1] . $user_ip_parts[2] . $user_ip_parts[3] . $user_ip_parts[4] . "', '" . $user_ip_parts[1] . $user_ip_parts[2] . $user_ip_parts[3] . "ff', '" . $user_ip_parts[1] . $user_ip_parts[2] . "ffff', '" . $user_ip_parts[1] . "ffffff')
    			OR ban_userid = $user_id";
    	if ( $user_id != ANONYMOUS )
    	{
    		$sql .= " OR ban_email LIKE '" . str_replace("\'", "''", $userdata['user_email']) . "'
    			OR ban_email LIKE '" . substr(str_replace("\'", "''", $userdata['user_email']), strpos(str_replace("\'", "''", $userdata['user_email']), "@")) . "'";
    	}
    	if ( !($result = $db->sql_query($sql)) )
    	{
    		message_die(CRITICAL_ERROR, 'Could not obtain ban information', '', __LINE__, __FILE__, $sql);
    	}
    
    	if ( $ban_info = $db->sql_fetchrow($result) )
    	{
    		if ( $ban_info['ban_ip'] || $ban_info['ban_userid'] || $ban_info['ban_email'] )
    		{
    			message_die(CRITICAL_MESSAGE, 'You_been_banned');
    		}
    	}
    
    	//
    	// Create or update the session
    	//
    	$sql = "UPDATE " . SESSIONS_TABLE . "
    		SET session_user_id = $user_id, session_start = $current_time, session_time = $current_time, session_page = $page_id, session_logged_in = $login, session_admin = $admin
    		WHERE session_id = '" . $session_id . "'
    			AND session_ip = '$user_ip'";
    	if ( !$db->sql_query($sql) || !$db->sql_affectedrows() )
    	{
    		$session_id = md5(dss_rand());
    
    		$sql = "INSERT INTO " . SESSIONS_TABLE . "
    			(session_id, session_user_id, session_start, session_time, session_ip, session_page, session_logged_in, session_admin)
    			VALUES ('$session_id', $user_id, $current_time, $current_time, '$user_ip', $page_id, $login, $admin)";
    		if ( !$db->sql_query($sql) )
    		{
    			message_die(CRITICAL_ERROR, 'Error creating new session', '', __LINE__, __FILE__, $sql);
    		}
    	}
    
    	if ( $user_id != ANONYMOUS )
    	{
    		$last_visit = ( $userdata['user_session_time'] > 0 ) ? $userdata['user_session_time'] : $current_time;
    
    		if (!$admin)
    		{
    			$sql = "UPDATE " . USERS_TABLE . "
    				SET user_session_time = $current_time, user_session_page = $page_id, user_lastvisit = $last_visit
    				WHERE user_id = $user_id";
    			if ( !$db->sql_query($sql) )
    			{
    				message_die(CRITICAL_ERROR, 'Error updating last visit time', '', __LINE__, __FILE__, $sql);
    			}
    		}
    
    		$userdata['user_lastvisit'] = $last_visit;
    
    		//
    		// Regenerate the auto-login key
    		//
    		if ($enable_autologin)
    		{
    			$auto_login_key = dss_rand() . dss_rand();
    			
    			if (isset($sessiondata['autologinid']) && (string) $sessiondata['autologinid'] != '')
    			{
    				$sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . "
    					SET last_ip = '$user_ip', key_id = '" . md5($auto_login_key) . "', last_login = $current_time
    					WHERE key_id = '" . md5($sessiondata['autologinid']) . "'";
    			}
    			else
    			{
    				$sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . "(key_id, user_id, last_ip, last_login)
    					VALUES ('" . md5($auto_login_key) . "', $user_id, '$user_ip', $current_time)";
    			}
    
    			if ( !$db->sql_query($sql) )
    			{
    				message_die(CRITICAL_ERROR, 'Error updating session key', '', __LINE__, __FILE__, $sql);
    			}
    			
    			$sessiondata['autologinid'] = $auto_login_key;
    			unset($auto_login_key);
    		}
    		else
    		{
    			$sessiondata['autologinid'] = '';
    		}
    
    // $sessiondata['autologinid'] = (!$admin) ? (( $enable_autologin && $sessionmethod == SESSION_METHOD_COOKIE ) ? $auto_login_key : '') : $sessiondata['autologinid'];
    		$sessiondata['userid'] = $user_id;
    	}
    
    	$userdata['session_id'] = $session_id;
    	$userdata['session_ip'] = $user_ip;
    	$userdata['session_user_id'] = $user_id;
    	$userdata['session_logged_in'] = $login;
    	$userdata['session_page'] = $page_id;
    	$userdata['session_start'] = $current_time;
    	$userdata['session_time'] = $current_time;
    	$userdata['session_admin'] = $admin;
    	$userdata['session_key'] = $sessiondata['autologinid'];
    
    	setcookie($cookiename . '_data', serialize($sessiondata), $current_time + 31536000, $cookiepath, $cookiedomain, $cookiesecure);
    	setcookie($cookiename . '_sid', $session_id, 0, $cookiepath, $cookiedomain, $cookiesecure);
    
    	$SID = 'sid=' . $session_id;
    
    	return $userdata;
    }
    
    //
    // Checks for a given user session, tidies session table and updates user
    // sessions at each page refresh
    //
    function session_pagestart($user_ip, $thispage_id)
    {
    	global $db, $lang, $board_config;
    	global $HTTP_COOKIE_VARS, $HTTP_GET_VARS, $SID;
    
    	$cookiename = $board_config['cookie_name'];
    	$cookiepath = $board_config['cookie_path'];
    	$cookiedomain = $board_config['cookie_domain'];
    	$cookiesecure = $board_config['cookie_secure'];
    
    	$current_time = time();
    	unset($userdata);
    
    	if ( isset($HTTP_COOKIE_VARS[$cookiename . '_sid']) || isset($HTTP_COOKIE_VARS[$cookiename . '_data']) )
    	{
    		$sessiondata = isset( $HTTP_COOKIE_VARS[$cookiename . '_data'] ) ? unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data'])) : array();
    		$session_id = isset( $HTTP_COOKIE_VARS[$cookiename . '_sid'] ) ? $HTTP_COOKIE_VARS[$cookiename . '_sid'] : '';
    		$sessionmethod = SESSION_METHOD_COOKIE;
    	}
    	else
    	{
    		$sessiondata = array();
    		$session_id = ( isset($HTTP_GET_VARS['sid']) ) ? $HTTP_GET_VARS['sid'] : '';
    		$sessionmethod = SESSION_METHOD_GET;
    	}
    
    	//
    	if (!preg_match('/^[A-Za-z0-9]*$/', $session_id))
    	{
    		$session_id = '';
    	}
    
    	$thispage_id = (int) $thispage_id;
    
    	//
    	// Does a session exist?
    	//
    	if ( !empty($session_id) )
    	{
    		//
    		// session_id exists so go ahead and attempt to grab all
    		// data in preparation
    		//
    		$sql = "SELECT u.*, s.*
    			FROM " . SESSIONS_TABLE . " s, " . USERS_TABLE . " u
    			WHERE s.session_id = '$session_id'
    				AND u.user_id = s.session_user_id";
    		if ( !($result = $db->sql_query($sql)) )
    		{
    			message_die(CRITICAL_ERROR, 'Error doing DB query userdata row fetch', '', __LINE__, __FILE__, $sql);
    		}
    
    		$userdata = $db->sql_fetchrow($result);
    
    		//
    		// Did the session exist in the DB?
    		//
    		if ( isset($userdata['user_id']) )
    		{
    			//
    			// Do not check IP assuming equivalence, if IPv4 we'll check only first 24
    			// bits ... I've been told (by vHiker) this should alleviate problems with
    			// load balanced et al proxies while retaining some reliance on IP security.
    			//
    			$ip_check_s = substr($userdata['session_ip'], 0, 6);
    			$ip_check_u = substr($user_ip, 0, 6);
    
    			if ($ip_check_s == $ip_check_u)
    			{
    				$SID = ($sessionmethod == SESSION_METHOD_GET || defined('IN_ADMIN')) ? 'sid=' . $session_id : '';
    
    				//
    				// Only update session DB a minute or so after last update
    				//
    				if ( $current_time - $userdata['session_time'] > 60 )
    				{
    					// A little trick to reset session_admin on session re-usage
    					$update_admin = (!defined('IN_ADMIN') && $current_time - $userdata['session_time'] > ($board_config['session_length']+60)) ? ', session_admin = 0' : '';
    
    					$sql = "UPDATE " . SESSIONS_TABLE . "
    						SET session_time = $current_time, session_page = $thispage_id$update_admin
    						WHERE session_id = '" . $userdata['session_id'] . "'";
    					if ( !$db->sql_query($sql) )
    					{
    						message_die(CRITICAL_ERROR, 'Error updating sessions table', '', __LINE__, __FILE__, $sql);
    					}
    
    					if ( $userdata['user_id'] != ANONYMOUS )
    					{
    						$sql = "UPDATE " . USERS_TABLE . "
    							SET user_session_time = $current_time, user_session_page = $thispage_id
    							WHERE user_id = " . $userdata['user_id'];
    						if ( !$db->sql_query($sql) )
    						{
    							message_die(CRITICAL_ERROR, 'Error updating sessions table', '', __LINE__, __FILE__, $sql);
    						}
    					}
    
    					session_clean($userdata['session_id']);
    
    					setcookie($cookiename . '_data', serialize($sessiondata), $current_time + 31536000, $cookiepath, $cookiedomain, $cookiesecure);
    					setcookie($cookiename . '_sid', $session_id, 0, $cookiepath, $cookiedomain, $cookiesecure);
    				}
    
    				// Add the session_key to the userdata array if it is set
    				if ( isset($sessiondata['autologinid']) && $sessiondata['autologinid'] != '' )
    				{
    					$userdata['session_key'] = $sessiondata['autologinid'];
    				}
    
    				return $userdata;
    			}
    		}
    	}
    
    	//
    	// If we reach here then no (valid) session exists. So we'll create a new one,
    	// using the cookie user_id if available to pull basic user prefs.
    	//
    	$user_id = ( isset($sessiondata['userid']) ) ? intval($sessiondata['userid']) : ANONYMOUS;
    
    	if ( !($userdata = session_begin($user_id, $user_ip, $thispage_id, TRUE)) )
    	{
    		message_die(CRITICAL_ERROR, 'Error creating user session', '', __LINE__, __FILE__, $sql);
    	}
    
    	return $userdata;
    
    }
    
    /**
    * Terminates the specified session
    * It will delete the entry in the sessions table for this session,
    * remove the corresponding auto-login key and reset the cookies
    */
    function session_end($session_id, $user_id)
    {
    	global $db, $lang, $board_config, $userdata;
    	global $HTTP_COOKIE_VARS, $HTTP_GET_VARS, $SID;
    
    	$cookiename = $board_config['cookie_name'];
    	$cookiepath = $board_config['cookie_path'];
    	$cookiedomain = $board_config['cookie_domain'];
    	$cookiesecure = $board_config['cookie_secure'];
    
    	$current_time = time();
    
    	if (!preg_match('/^[A-Za-z0-9]*$/', $session_id))
    	{
    		return;
    	}
    	
    	//
    	// Delete existing session
    	//
    	$sql = 'DELETE FROM ' . SESSIONS_TABLE . "
    		WHERE session_id = '$session_id'
    			AND session_user_id = $user_id";
    	if ( !$db->sql_query($sql) )
    	{
    		message_die(CRITICAL_ERROR, 'Error removing user session', '', __LINE__, __FILE__, $sql);
    	}
    
    	//
    	// Remove this auto-login entry (if applicable)
    	//
    	if ( isset($userdata['session_key']) && $userdata['session_key'] != '' )
    	{
    		$autologin_key = md5($userdata['session_key']);
    		$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
    			WHERE user_id = ' . (int) $user_id . "
    				AND key_id = '$autologin_key'";
    		if ( !$db->sql_query($sql) )
    		{
    			message_die(CRITICAL_ERROR, 'Error removing auto-login key', '', __LINE__, __FILE__, $sql);
    		}
    	}
    
    	//
    	// We expect that message_die will be called after this function,
    	// but just in case it isn't, reset $userdata to the details for a guest
    	//
    	$sql = 'SELECT *
    		FROM ' . USERS_TABLE . '
    		WHERE user_id = ' . ANONYMOUS;
    	if ( !($result = $db->sql_query($sql)) )
    	{
    		message_die(CRITICAL_ERROR, 'Error obtaining user details', '', __LINE__, __FILE__, $sql);
    	}
    	if ( !($userdata = $db->sql_fetchrow($result)) )
    	{
    		message_die(CRITICAL_ERROR, 'Error obtaining user details', '', __LINE__, __FILE__, $sql);
    	}
    	$db->sql_freeresult($result);
    
    
    	setcookie($cookiename . '_data', '', $current_time - 31536000, $cookiepath, $cookiedomain, $cookiesecure);
    	setcookie($cookiename . '_sid', '', $current_time - 31536000, $cookiepath, $cookiedomain, $cookiesecure);
    
    	return true;
    }
    
    /**
    * Removes expired sessions and auto-login keys from the database
    */
    function session_clean($session_id)
    {
    	global $board_config, $db;
    
    	//
    	// Delete expired sessions
    	//
    	$sql = 'DELETE FROM ' . SESSIONS_TABLE . '
    		WHERE session_time < ' . (time() - (int) $board_config['session_length']) . "
    			AND session_id <> '$session_id'";
    	if ( !$db->sql_query($sql) )
    	{
    		message_die(CRITICAL_ERROR, 'Error clearing sessions table', '', __LINE__, __FILE__, $sql);
    	}
    
    	//
    	// Delete expired auto-login keys
    	// If max_autologin_time is not set then keys will never be deleted
    	// (same behaviour as old 2.0.x session code)
    	//
    	if (!empty($board_config['max_autologin_time']) && $board_config['max_autologin_time'] > 0)
    	{
    		$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
    			WHERE last_login < ' . (time() - (86400 * (int) $board_config['max_autologin_time']));
    		$db->sql_query($sql);
    	}
    
    	return true;
    }
    
    /**
    * Reset all login keys for the specified user
    * Called on password changes
    */
    function session_reset_keys($user_id, $user_ip)
    {
    	global $db, $userdata, $board_config;
    
    	$key_sql = ($user_id == $userdata['user_id'] && !empty($userdata['session_key'])) ? "AND key_id != '" . md5($userdata['session_key']) . "'" : '';
    
    	$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
    		WHERE user_id = ' . (int) $user_id . "
    			$key_sql";
    
    	if ( !$db->sql_query($sql) )
    	{
    		message_die(CRITICAL_ERROR, 'Error removing auto-login keys', '', __LINE__, __FILE__, $sql);
    	}
    
    	$where_sql = 'session_user_id = ' . (int) $user_id;
    	$where_sql .= ($user_id == $userdata['user_id']) ? " AND session_id <> '" . $userdata['session_id'] . "'" : '';
    	$sql = 'DELETE FROM ' . SESSIONS_TABLE . "
    		WHERE $where_sql";
    	if ( !$db->sql_query($sql) )
    	{
    		message_die(CRITICAL_ERROR, 'Error removing user session(s)', '', __LINE__, __FILE__, $sql);
    	}
    
    	if ( !empty($key_sql) )
    	{
    		$auto_login_key = dss_rand() . dss_rand();
    
    		$current_time = time();
    		
    		$sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . "
    			SET last_ip = '$user_ip', key_id = '" . md5($auto_login_key) . "', last_login = $current_time
    			WHERE key_id = '" . md5($userdata['session_key']) . "'";
    		
    		if ( !$db->sql_query($sql) )
    		{
    			message_die(CRITICAL_ERROR, 'Error updating session key', '', __LINE__, __FILE__, $sql);
    		}
    
    		// And now rebuild the cookie
    		$sessiondata['userid'] = $user_id;
    		$sessiondata['autologinid'] = $auto_login_key;
    		$cookiename = $board_config['cookie_name'];
    		$cookiepath = $board_config['cookie_path'];
    		$cookiedomain = $board_config['cookie_domain'];
    		$cookiesecure = $board_config['cookie_secure'];
    
    		setcookie($cookiename . '_data', serialize($sessiondata), $current_time + 31536000, $cookiepath, $cookiedomain, $cookiesecure);
    		
    		$userdata['session_key'] = $auto_login_key;
    		unset($sessiondata);
    		unset($auto_login_key);
    	}
    }
    
    //
    // Append $SID to a url. Borrowed from phplib and modified. This is an
    // extra routine utilised by the session code above and acts as a wrapper
    // around every single URL and form action. If you replace the session
    // code you must include this routine, even if it's empty.
    //
    function append_sid($url, $non_html_amp = false)
    {
    	global $SID;
    
    	if ( !empty($SID) && !preg_match('#sid=#', $url) )
    	{
    		$url .= ( ( strpos($url, '?') !== false ) ?  ( ( $non_html_amp ) ? '&' : '&amp;' ) : '?' ) . $SID;
    	}
    
    	return $url;
    }