Jump to content

Memory exhausted because of render()

Recommended Posts

I am creating a module that sends pages as en email. I use ->render() to get the content of a PW page and send it as the body of the mail. But something is wrong and I am running out of memory (currently set to 100 MB).

This function checks, if there is a page flagged for sending. $mailtempplate is the corpus delicti …

public function checkSchedule($event) {

		// Memory usage
		$memory = 'Memory Start: ' . memory_get_usage();

		// Find pages, that are scheduled and have a schedule date that is older than current time plus 60 seconds
		$scheduled_pages = wire('pages')->find('template=newsletter-mailing, schedule=1, schedule_date<=' . time() + 60);

		// Check if there are scheduled pages, and stop, if nothing is scheduled
		if (!count($scheduled_pages)) {

		// Iterate scheduled pages
		foreach ($scheduled_pages as $scheduled_page) {

			// Get the mail-template, i.e. the rendered page (this becomes the mail-body later)
                        // If I just use $mailtemplate = 'Test'; everything works fine
			$mailtemplate = $scheduled_page->render();

			// Convert local/relative paths to absolute URL for links (href) and images (src)
			if (!strpos($mailtemplate,'href="/') === false) {
				$mailtemplate = str_replace('href="/', 'href="' . wire('config')->httpUrl, $mailtemplate);
			if (!strpos($mailtemplate,'src="/') === false) {
				$mailtemplate = str_replace('src="/', 'src="' . wire('config')->httpUrl, $mailtemplate);

			// Dispatch this scheduled page ("Newsletter") for email
			$dispatched = $this->dispatchMailing($scheduled_page->title, $scheduled_page->newsletter_recipients, $mailtemplate);
			if ($dispatched) {
				$memory .= ' und Memory Ende: ' . memory_get_usage();

				// Send reporting mail
			} else {
				// Something went wrong while sending

			// Uncheck schedule checkbox, even if there was an error whiele sending!
			$p = $scheduled_page;
			$p->schedule = 0;

			// Clear the mailtemplate for the next scheduled page/mailing

I moved $mailtemplate = $scheduled_page->render() out of the foreach() but it doesn't work either.

Appreciate any help and tips :-)

Share this post

Link to post
Share on other sites

Is memory exhausted on the first call to $scheduled_page->render() or are you handling a large amount of these pages?

Depending on the amount of pages you're fetching and rendering, 100 MB getting exhausted doesn't sound too surprising. You could try running wire('pages')->uncacheAll() every once in a while -- it will result in slower execution times, but will also free the memory.

Share this post

Link to post
Share on other sites

teppo, thanks for your help!

There is just one page that is scheduled for sending, so it is on first call of render(). The scheduled page (which I want to render) has only 3 fields and currently 1 child with 4 fields. I would say it is just a typical webpage, nothing special.

I put wire('pages')->uncacheAll(); below my unset($mailtemplate); but it doesn't help.

Share this post

Link to post
Share on other sites

Perhaps I found the error. I had this in my module init() …

$this->addHookAfter('Page::render', $this, 'checkSchedule');

That ends in an infinite loop, since every time I called my render() template the hook fires my module again.

What hook should I use instead? The LazyCron?

Share this post

Link to post
Share on other sites

LazyCron is one option, though you might consider running this with regular cron too. LazyCron is (in some cases) easier to setup, but has to be triggered by user.. and because it's going to be tied to that user's session, it's also prone to problems when used for something slow-running.

One simple way to work with regular cron is creating a script that bootstraps ProcessWire, loads your module and executes a method:

require "index.php"; // ProcessWire's index.php

After that you'll just have to setup a cron job (usually with crontab, but that depends on your environment) to run this script regularly with PHP (something like "5 0 * * * /usr/bin/php /path/to/script.php", which would run the script every day five minutes after midnight) and you're all set up.

.. and if you still want to use LazyCron, you could always hook into LazyCron::everyDay, LazyCron::everyHour etc. depending on your needs. Just be aware that this method may cause issues for you in the long run.

Share this post

Link to post
Share on other sites

That sounds like a good solution. I first thought to use LazyCron and point a cron job to the homepage.

Thanks teppo!

Share this post

Link to post
Share on other sites

Render hook with a render inside results in endless recursion.

You can avoid it by setting a property to the page rendered already and check for it in the start of the hook, and if this property is true, skip it.

if($p->skipRender) return;
$p->skipRender = true;

Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Martin Wuehr
      Hello everybody,
      I'm hosting a Processwire Installation for a client. It runs on Processwire 2.5.3. Over the years everything was fine but now there is an issue with one Image-Field in one Template. Trying to upload an Image the Error "Not enough Memory to load/resize" occure. Other Image-Fields in all other Templates working without any issue. The Images I Tried to upload are very small (600px * 420px). The Images blongs to a News feed, and worked before.
      I read about the problem in the forum but I can't get this solved. I have no access to the php.ini to modify the memory_limit.
      I tried to update the Processwire Verion but failed. (Tried 2.7 /2.8 / 3.2) Got Always Internal Server Error and in the error Log: 
      Compile Error:     Cannot redeclare PageEditFieldPermissionConfig()….
      Has anybody any idea how I can get this solved?
      Thank you so much
    • By rareyush
      <?php namespace ProcessWire; $form = $modules->get('FormTemplateProcessor');$form->template = $templates->get('book-now'); // required $form->requiredFields = array('title', 'email', 'number','body'); //$form->email = 'your@email.com'; // optional, sends form as email $form->parent = $page('template=book-now'); // optional, saves form as page //$form->render(); // draw form or process submitted form ?> <?php echo $form->render(); ?>  
      using this to for a contact on new processwire version 3  and I am receiving this error
      Error: Uncaught Error: Call to undefined method stdClass::render() in C:\wamp64\www\mbs\site\templates\book-now.php:9  
      tried following but none of them works
      anyone ?  
    • By Lance O.
      I've built a site that uses parent pages as the "container" for child pages:
      Parent page [page.php] -- Child page [unit-child-a.php] -- Child page [unit-child-b.php] ---- Grandchild page [subunit-grandchild-a.php] ---- Grandchild page [subunit-grandchild-b.php] -- Child page [unit-child-c.php] The child pages are displayed as content on the corresponding parent page using the render method. Some child pages also include their own child pages. (Internally the child page template names start with "unit-" and the grandchild page template names start with "subunit-".)
      foreach ($page->children("template=template-name,sort=sort") as $child) echo $child->render(); This approach allows content administrators to add and reorder child pages as necessary, but I've come across an issue that I'm not sure how to address.
      The site includes search functionality, but when a visitor searches for content and it is found in a child or grandchild page, the Parent page should be the one that is displayed in the search results, since the child and grandchild pages are not directly viewable. Since some searches could include more than one child or grandchild page of a parent page, this sometimes means that a parent page could be included more than once.
      How do I easily redirect the child and grandchild pages to the parent page, and only include the parent page once in the search results?
    • By ov3rtak3r
      Hello everyone!
      I am new to Processwire and really like it, but now I have a problem that I can't solve by myself.
      I have created following templates:
      data_people (no template-file)
      person_listing (person_listing.php)
      person_detail (person_detail.php)

      I have added the following Pages:
      Actor, Director, Stuff using template person_listing.php [visible in frontend]
      "People" using template data_people [not visible, only for data]
      -> "Georg Klonee" using template person_detail [is visible, but should not be shown using path /en/person/georg-klonee]

      Example Person: Georg Klonee
      The Problem:
      He is a Actor and a Director, so I want him to be shown in the Listing of /en/actors and /en/directors. No problem until here - it works. But now, when I click him, the URL for the Detail-Page is unfortunately /en/person/georg-klonee. But I want it to be /en/actors/georg-klonee, if i open the details from Actors-Page (and of course /en/directors/georg-klonee when on Directors-Page).
      How can I get this to work? I am also using TemplateEngine-Module with TemplateTwigEngine-Module, hopefully this won't be a problem? I tried it with URL-Segments for person_listing.php. But how can I abort the rendering of people_listing and start rendering person_detail.php?
      Thanks for your help in advance.
    • By creativejay
      I'm displaying a list of products which are found by their templates, but the pages are taking a very long time to load. At first, I blamed it on my image rendering (using PIM2), but even with all those images now stored in the file tree, the page is taking abysmally long to load. ProCache seems to help but I don't feel as though what I'm trying to do should be gnawing the bones of my resources quite so long.
      The variable for the selector is defined in my header include:
      $productCatList="prod_series|prod_series_ethernet|prod_series_access|prod_series_accessories|prod_series_fiber|prod_series_pwr_supplies|prod_series_pwr_systems|prod_series_wireless"; $getCurrentProdOptions="template=$productCatList, prod_status_pages!=1554|1559|1560|4242"; Then in the template for the page upon which the directory loads:
      $products = $pages->find("$getCurrentProdOptions"); include_once("./prod-list-row.inc"); echo $out; And the prod-list-row.inc foreach (which is on every page that's exhibiting the slowdown):
      <?php $sum = 0; $out =""; $out .= "<div class='span_12_of_12'>\n"; foreach($products as $p){ $sum += 1; if ($sum % 2 == 0) { $bgcolor = '#fff'; } else { $bgcolor = '#e4e4e4';} $par = $p->parent; $out .="<div class='section group' style='background: $bgcolor ; min-height: 110px'>\n"; $img = $p->prod_image; $thumb = $img->pim2Load('squarethumb100')->canvas(100,100,array(0,0,0,0),'north',0)->pimSave()->url; $out .="<div data-match-height='{$p->title}' class='col span_2_of_12 hide'>"; $out .="<a href='{$p->url}'><span class='product-image-box'><img src='{$thumb}' alt='{$p->title}' title='{$p->title}'></span></a>"; $out .= "</div>"; $out .= "<div data-match-height='{$p->title}' class='col span_6_of_12'>"; $out .= "<div class='prod-list-name-label'><a href='{$p->url}'>{$p->title}</a></div>"; if($page!=$par) { $out .= "<div class='prod-list-category-label' style='font-size: .7em;'>Category: <a href='{$par->url}'>{$par->title}</a></div>"; } $out .= "<div class='list-headline' style='font-size: .8em;'>{$p->headline}</div>"; $out .="<div class='learn-more-buttons-sm'>"; $out .="<a href='{$p->url}' title='Product Specs and Documentation'><span class='find-out-more-button' style='font-size: .8em;'><i style='font-size: .8em;' class='fa fa-lightbulb-o' ></i> &nbsp; Learn More</span></a>"; $out .="</div>"; $out .="</div> \n"; $out .= " <div data-match-height='{$p->title}' class='col span_4_of_12'>"; if(count($p->prod_feat_imgs) >0 ){ $out .= "<div class='featured-icons-list' margin: 2em .5em;'>"; foreach($p->prod_feat_imgs as $feat){ $icon = $pages->get("$feat->prod_featicon_pages"); if($icon->image) { if($feat->prod_feat_textlang) { $icontitle = $feat->prod_feat_textlang;} else {$icontitle = $icon->title;} $out .= "<img src='".$icon->image->size(35,35,$imgOptions)->url . "' alt='" . $icontitle . "' title='" . $icontitle . "' class='listing-feat-icon' style='margin-right: .5em;' />"; } } $out .= "</div>"; if($p->prod_product_line){ foreach($p->prod_product_line as $pline) if($pline->image) { $out .= "<div style='height: 35px;'>\n"; $out .= "<img src='{$pline->image->size(75,35,$imgOptions)->url}' alt='{$pline->title}' />"; $out .= "</div>"; } } } $out .= "</div>"; $out .="</div>"; } $out .= "</div>";  
      Is there a clear culprit here of what I'm doing that's so stressing the system?
      I turned off TracyDebugger because I saw another thread about that causing slowdown (even though I'm using the latest), but that had no effect. Every time I thought I found the culprit and commented it out, nothing changed.
      Would appreciate some more eyes on this. Thank you!
      ETA: prod_feat_imgs is a repeater field which contains a Page reference field (from which I pull the image and title) and a multilanguage textfield (to override the page reference title if it exists). Could that be the problem?
  • Create New...